在Java中实现Visitor模式 [英] Generified implementation of Visitor pattern in Java

查看:81
本文介绍了在Java中实现Visitor模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经做了一些研究,尝试开发一种类型转换框架,它提供了将源类(例如Foo)的实例转换成结果类的实例(例如,Bar或Baz)的能力。该框架应该为同一对源和结果提供使用不同转换逻辑(即不同转换器)的能力。它也应该是可扩展的,即允许为新的和现有的源和结果对添加新的转换器。另一个要求是类型安全,即任何尝试将一些源类的实例转换为结果类的实例,而不需要转换器实现适当的转换逻辑就会导致编译时错误。



我决定使用访问者模式,转换器作为访问者和可转换类作为元素。为了提供可扩展性和类型安全性,我决定使用泛型。所以我所做的转换框架的第一次实现受到互联网上的一些文章的影响(不幸的是我失去了链接)是...



转换框架与statefull转换器



以下是框架的核心界面,转换器和可转换:

  public interface Converter< V extends Converter< V,A>,A extends Convertable< V,A> {

void convert(A convertable);
}


public interface Convertable< V extends Converter< V,A>,A extends Convertable< V,A> {

void convertWith(V​​ converter);
}

泛型使可转换只接受 Converter 的实现,可以转换它们并实现 Converter 只访问 Convertable 它是为了转换。以下是这样的转换器的示例:

  interface FooConverter扩展Converter&FooConverter,Foo> {

void convert(Foo convertable);

void convert(FooChild1 convertable);

void convert(FooChild2 convertable);
}


public class Foo2BarConverter实现FooConverter {

private Bar result;

public Bar getResult(){
return result;
}

@Override
public void convert(Foo convertable){
this.result = new Bar(此条从Foo的实例转换);
}

@Override
public void convert(FooChild1 convertable){
this.result = new Bar(此条从FooChild1的一个实例转换);
}

@Override
public void convert(FooChild2 convertable){
this.result = new Bar(此条从FooChild2的实例转换);
}
}


public class Foo2BazConverter实现FooConverter {

私人Baz结果;

public Baz getResult(){
return result;
}

@Override
public void convert(Foo convertable){
this.result = new Baz(这个baz从Foo的一个实例转换);
}

@Override
public void convert(FooChild1 convertable){
this.result = new Baz(这个baz从FooChild1的一个实例转换);
}

@Override
public void convert(FooChild2 convertable){
this.result = new Baz(这个baz从FooChild2的一个实例转换);
}
}

这里有一些类可以转换为转换器:

  public class Foo implements Convertable&FooConverter,Foo> {

@Override
public void convertWith(FooConverter converter){
converter.convert(this);
}
}


public class FooChild1 extends Foo {

@Override
public void convertWith(FooConverter converter){
converter.convert(this);
}
}


public class FooChild2 extends Foo {

@Override
public void convertWith(FooConverter converter){
converter.convert(this);
}
}

这里是结果类,即 Bar Baz

  public class Bar {

private String message;

公共栏(String message){
this.message = message;
}

public String getMessage(){
return message;
}
}


public class Baz {

private String message;

public Baz(String message){
this.message = message;
}

public String getMessage(){
return message;
}
}

这里是一个测试转换器的代码: / p>

  Foo fooObj = new Foo(); 
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

//转换为bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();

fooObj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult()。getMessage());

fooChild1Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult()。getMessage());

fooChild2Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult()。getMessage());

//转换为baz
System.out.println();
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();

fooObj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult()。getMessage());

fooChild1Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult()。getMessage());

fooChild2Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult()。getMessage());

此代码生成的输出

 此条从Foo 
的实例转换此条从FooChild1的实例转换
此条从FooChild2 $ b的实例转换
$ b这个baz从Foo
的一个实例转换这个baz从FooChild1的一个实例转换为
这个baz从一个FooChild2的实例转换
/ pre>

查看中的结果字段Foo2BarConverter Foo2BazConverter 。这是实现的主要缺点。这使得转换器状态不总是方便。试图避免这个缺点我开发了...



没有双重调度的转换框架



这个实现的要点是参数化转换器,其结果类和返回结果来自 convert 方法 Converter convertWith 方法可转换。以下是代码中的代码:

  public interface Converter< A extends Convertable< A>,R> {

R convert(A convertable);
}

public interface Convertable< A extends Convertable< A>> {

< R> R convertWith(Converter< A,R>转换器);
}

public interface FooConverter< R>扩展转换器< Foo,R> {

@Override
R convert(Foo convertable);

R convert(FooChild1 convertable);

R convert(FooChild2 convertable);
}

public class Foo2BarConverter implements FooConverter< Bar> {

@Override
public Bar convert(Foo convertable){
return new Bar(此条从Foo的实例转换);
}

@Override
public Bar convert(FooChild1 convertable){
return new Bar(此条从FooChild1的一个实例转换);
}

@Override
public Bar convert(FooChild2 convertable){
return new Bar(此条从FooChild2的实例转换);
}
}

public class Foo2BazConverter实现FooConverter< Baz> {

@Override
public Baz convert(Foo convertable){
return new Baz(这个baz从Foo的一个实例转换);
}

@Override
public Baz convert(FooChild1 convertable){
return new Baz(这个baz从FooChild1的一个实例转换);
}

@Override
public Baz convert(FooChild2 convertable){
return new Baz(这个baz从FooChild2的一个实例转换);
}
}

public class Foo implements Convertable< Foo> {

@Override
public< R> R convertWith(Converter< Foo,R>转换器){
return converter.convert(this);
}
}

public class FooChild1 extends Foo {

@Override
public< R> R convertWith(Converter< Foo,R>转换器){
return converter.convert(this);
}
}

public class FooChild2 extends Foo {

@Override
public< R> R convertWith(Converter< Foo,R>转换器){
return converter.convert(this);
}
}

V Convertable 声明中删除,因为在 Converter 声明中的结果类将使我们实际上参数化实现的可转换与结果类。它会将可转换的每个实现绑定到唯一可以转换为的结果类。所以 convertWith in Convertable 指接收的转换器与转换器< A,R> 界面。这就是问题。现在执行 Convertable 调用收到的转换器将会调用 convert ,它在 Converter 接口,而不是 convert 方法,它在转换器实现中覆盖它。换句话说, convert(FooChild1 convertable) convert(FooChild2 convertable) Foo2BarConverter Foo2BazConverter 永远不会被调用。基本上,它杀死了访客模式的主要概念,双重调度。这是一个测试代码...

  Foo fooObj = new Foo(); 
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

//转换为bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BarConverter).getMessage());

System.out.println();

//转换为baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BazConverter).getMessage());

及其输出,表明在此实现中未调用重写方法。

 此条从Foo 
的实例转换此条从Foo
的实例转换这个酒吧从Foo

的一个实例转换这个baz从Foo
的一个实例转换这个baz从Foo
的一个实例转换这个baz从Foo $ b的一个实例转换$ b

下一个实现我试图使无状态转换器是...



具有参数化方法的转换器



这里的主要概念是仅参数化方法,我想在没有参数的情况下返回转换结果

  public interface Converter< V extends Converter< V,A>,A extends Convertable< V,A>> ; {

< R> R转换(A转换);
}

public interface Convertable< V extends Converter< V,A>,A extends Convertable< V,A> {

< R> R转换(V转换器);
}

接口FooConverter扩展Converter&FooConverter,Foo> {

< R> R转换(Foo可转换);

< R> R转换(FooChild1可转换);

< R> R转换(FooChild2可转换);
}

public class Foo2BarConverter实现FooConverter {

@Override
public Bar convert(Foo convertable){
return new Bar(这个酒吧从Foo的一个实例转换成);
}

@Override
public Bar convert(FooChild1 convertable){
return new Bar(此条从FooChild1的一个实例转换);
}

@Override
public Bar convert(FooChild2 convertable){
return new Bar(此条从FooChild2的实例转换);
}
}

public class Foo2BazConverter实现FooConverter {

@Override
public Baz convert(Foo convertable){
返回新的Baz(这个baz从Foo的一个实例转换);
}

@Override
public Baz convert(FooChild1 convertable){
return new Baz(这个baz从FooChild1的一个实例转换);
}

@Override
public Baz convert(FooChild2 convertable){
return new Baz(这个baz从FooChild2的一个实例转换);
}
}

public class Foo实现Convertable&FooConverter,Foo> {

@Override
public< R> R convertWith(FooConverter转换器){
return converter.convert(this);
}
}

public class FooChild1 extends Foo {

@Override
public< R> R convertWith(FooConverter转换器){
return converter.convert(this);
}
}

public class FooChild2 extends Foo {

@Override
public< R> R convertWith(FooConverter转换器){
return converter.convert(this);
}
}

测试代码

  Foo fooObj = new Foo(); 
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

//转换为bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj。< Bar> convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj。< Bar> convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj。< Bar> convertWith(foo2BarConverter).getMessage());

System.out.println();

//转换为baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj。< Baz> convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj。< Baz> convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj。< Baz> convertWith(foo2BazConverter).getMessage());

并输出

 此条从Foo 
的实例转换此条从FooChild1的实例转换为
此条从FooChild2的实例转换为

这个baz从Foo
的一个实例转换这个baz从FooChild1的一个实例转换成
这个baz从FooChild2的一个实例转换成

乍看起来看起来不错。但实际上这个解决方案并不是安全的。例如以下调用

  fooObj。< Baz> convertWith(foo2BarConverter).getMessage()

不会导致编译时错误。但是在运行时会导致ClassCastException。



所以一般问题如下。



有没有办法使Java成为无状态的安全访问者?



UPD:我添加了所有三个实现:第一个 2nd 3rd

解决方案

你的转换器是简单的函数,你可能不需要一个框架来组成它们。而你的第三次尝试没有什么意义:

 < R> R转换(V转换器);意思是:给出一些东西(V转换器,你不需要知道R的任何东西),给我任意(任意R)。你发现这不行。



使用更正的访问者模式

 界面FooConverter< R>扩展功能 {

R convert(Foo convertable);

R convert(FooChild1 convertable);

R convert(FooChild2 convertable);

default R apply(Foo foo){return foo.convertWith(this); }
}

public class Foo2BarConverter implements FooConverter< Bar> {

@Override
public Bar convert(Foo convertable){
return new Bar(此条从Foo的实例转换);
}

@Override
public Bar convert(FooChild1 convertable){
return new Bar(此条从FooChild1的一个实例转换);
}

@Override
public Bar convert(FooChild2 convertable){
return new Bar(此条从FooChild2的一个实例转换);
}
}

public class Foo2BazConverter实现FooConverter< Baz> {

@Override
public Baz convert(Foo convertable){
return new Baz(这个baz从Foo的一个实例转换);
}

@Override
public Baz convert(FooChild1 convertable){
return new Baz(这个baz从FooChild1的一个实例转换);
}

@Override
public Baz convert(FooChild2 convertable){
return new Baz(这个baz从FooChild2的一个实例转换);
}
}

public class Foo {

public< R> R convertWith(FooConverter R转换器){
return converter.convert(this);
}
}

public class FooChild1 extends Foo {

@Override
public< R> R convertWith(FooConverter R转换器){
return converter.convert(this);
}
}

public class FooChild2 extends Foo {

@Override
public< R> R convertWith(FooConverter R转换器){
return converter.convert(this);
}
}

public void test(){
Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

//转换为bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BarConverter).getMessage());

System.out.println();

//转换为baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BazConverter).getMessage());

//不编译:
fooObj。< Baz> convertWith(foo2BarConverter).getMessage();
}

然后如果你想要一些更多的框架,你可能想要研究镜头: https://github.com/functionaljava / functionaljava / tree / master / core / src / main / java / fj / data / optic


I've made some research trying to develop a type conversion framework which provides an ability to convert instances of a source class (e.g., Foo) to instances of result classes (e.g., Bar or Baz). The framework should provide an ability to use different conversion logic (i.e., different converters) for the same pair of a source and result. It also should be extendable, i.e. allow of adding new converters for new and existing pairs of a source and result. One more requirement is typesafety, i.e. any attempt to convert an instance of some source class to an instance of a result class without converter implementing appropriate conversion logic should lead to compile time error.

I decided to use a Visitor pattern with converters as Visitors and convertable classes as Elements. To provide extendability and typesafety I decided to use generics. So the first implementation of the conversion framework I made being influenced by some article in the Internet (unfortunately I've lost the link) was...

Conversion framework with statefull converters

Here are the core interfaces of the framework, Converter and Convertable:

    public interface Converter<V extends Converter<V,A>, A extends Convertable<V,A>> {

        void convert(A convertable);
    }


    public interface Convertable<V extends Converter<V,A>, A extends Convertable<V,A>> {

        void convertWith(V converter);
    }

Generics make an implementation of Convertable accept only implementations of Converter which can convert them and make an implementation of Converter visit only implementations of Convertable which it's made to convert. Here is an example of such converters:

interface FooConverter extends Converter<FooConverter,Foo> {

    void convert(Foo convertable);

    void convert(FooChild1 convertable);

    void convert(FooChild2 convertable);
}


public class Foo2BarConverter implements FooConverter {

    private Bar result;

    public Bar getResult() {
        return result;
    }

    @Override
    public void convert(Foo convertable) {
        this.result = new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public void convert(FooChild1 convertable) {
        this.result = new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public void convert(FooChild2 convertable) {
        this.result = new Bar("This bar's converted from an instance of FooChild2");
    }
}


public class Foo2BazConverter implements FooConverter {

    private Baz result;

    public Baz getResult() {
        return result;
    }

    @Override
    public void convert(Foo convertable) {
        this.result = new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public void convert(FooChild1 convertable) {
        this.result = new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public void convert(FooChild2 convertable) {
        this.result = new Baz("This baz's converted from an instance of FooChild2");
    }
}

And here are some classes which could be converted with that converters:

public class Foo implements Convertable<FooConverter, Foo> {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}


public class FooChild1 extends Foo {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}


public class FooChild2 extends Foo {

    @Override
    public void convertWith(FooConverter converter) {
        converter.convert(this);
    }
}

Here are result classes, i.e. Bar and Baz:

public class Bar {

    private String message;

    public Bar(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}


public class Baz {

    private String message;

    public Baz(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

And here is a code which tests that converters:

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();

fooObj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

fooChild1Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

fooChild2Obj.convertWith(foo2BarConverter);
System.out.println(foo2BarConverter.getResult().getMessage());

// converting to baz
System.out.println();
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();

fooObj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

fooChild1Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

fooChild2Obj.convertWith(foo2BazConverter);
System.out.println(foo2BazConverter.getResult().getMessage());

and output built by this code

This bar's converted from an instance of Foo
This bar's converted from an instance of FooChild1
This bar's converted from an instance of FooChild2

This baz's converted from an instance of Foo
This baz's converted from an instance of FooChild1
This baz's converted from an instance of FooChild2

Take a look at result field in Foo2BarConverter and Foo2BazConverter. That's the main drawback of the implementation. It makes converters statefull which is not always handy. Trying to avoid this drawback I developed...

Conversion framework without double dispatching

The main point of this implementation is to parametrize converters with result classes and return results from convert method of Converter and convertWith method of Convertable. Here is how it looks in code:

public interface Converter<A extends Convertable<A>,R> {

    R convert(A convertable);
}

public interface Convertable<A extends Convertable<A>> {

    <R> R convertWith(Converter<A,R> converter);
}

public interface FooConverter<R> extends Converter<Foo,R> {

    @Override
    R convert(Foo convertable);

    R convert(FooChild1 convertable);

    R convert(FooChild2 convertable);
}

public class Foo2BarConverter implements FooConverter<Bar> {

    @Override
    public Bar convert(Foo convertable) {
        return new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public Bar convert(FooChild1 convertable) {
        return new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public Bar convert(FooChild2 convertable) {
        return new Bar("This bar's converted from an instance of FooChild2");
    }
}

public class Foo2BazConverter implements FooConverter<Baz> {

    @Override
    public Baz convert(Foo convertable) {
        return new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public Baz convert(FooChild1 convertable) {
        return new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public Baz convert(FooChild2 convertable) {
        return new Baz("This baz's converted from an instance of FooChild2");
    }
}

public class Foo implements Convertable<Foo> {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

public class FooChild1 extends Foo {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

public class FooChild2 extends Foo {

    @Override
    public <R> R convertWith(Converter<Foo,R> converter) {
        return converter.convert(this);
    }
}

V is removed from Convertable declaration because having a result class in Converter declaration would have us in fact to parametrize implementations of Convertable with result classes. It would bound each implementation of convertable to the only result class it could be converted to. So convertWith in Convertable refers received converters with Converter<A,R> interface. And that's the problem. Now implementations of Convertable invoking received converter will allways invoke convert which is defined in Converter interface and not convert methods which override it in Converter implementations. In other words convert(FooChild1 convertable) and convert(FooChild2 convertable) in Foo2BarConverter and Foo2BazConverter will never be called. Basically, it kills the main notion of Visitor pattern, double dispatching. Here is a test code...

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BarConverter).getMessage());

System.out.println();

// converting to baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.convertWith(foo2BazConverter).getMessage());

and its output which demonstrates that overriding methods aren't called in this implementation.

This bar's converted from an instance of Foo
This bar's converted from an instance of Foo
This bar's converted from an instance of Foo

This baz's converted from an instance of Foo
This baz's converted from an instance of Foo
This baz's converted from an instance of Foo

Next implementation I tried to make stateless converters with was...

Converters with parametrized methods

The main notion here is to parametrize only methods which I want to return a conversion result without parametrizing declarations of interfaces.

public interface Converter<V extends Converter<V,A>, A extends Convertable<V,A>> {

    <R> R convert(A convertable);
}

public interface Convertable<V extends Converter<V,A>, A extends Convertable<V,A>> {

    <R> R convertWith(V converter);
}

interface FooConverter extends Converter<FooConverter,Foo> {

    <R> R convert(Foo convertable);

    <R> R convert(FooChild1 convertable);

    <R> R convert(FooChild2 convertable);
}

public class Foo2BarConverter implements FooConverter {

    @Override
    public Bar convert(Foo convertable) {
        return new Bar("This bar's converted from an instance of Foo");
    }

    @Override
    public Bar convert(FooChild1 convertable) {
        return new Bar("This bar's converted from an instance of FooChild1");
    }

    @Override
    public Bar convert(FooChild2 convertable) {
        return new Bar("This bar's converted from an instance of FooChild2");
    }
}

public class Foo2BazConverter implements FooConverter {

    @Override
    public Baz convert(Foo convertable) {
        return new Baz("This baz's converted from an instance of Foo");
    }

    @Override
    public Baz convert(FooChild1 convertable) {
        return new Baz("This baz's converted from an instance of FooChild1");
    }

    @Override
    public Baz convert(FooChild2 convertable) {
        return new Baz("This baz's converted from an instance of FooChild2");
    }
}

public class Foo implements Convertable<FooConverter, Foo> {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

public class FooChild1 extends Foo {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

public class FooChild2 extends Foo {

    @Override
    public <R> R convertWith(FooConverter converter) {
        return converter.convert(this);
    }
}

Testing code

Foo fooObj = new Foo();
Foo fooChild1Obj = new FooChild1();
Foo fooChild2Obj = new FooChild2();

// converting to bar
Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
System.out.println(fooObj.<Bar>convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild1Obj.<Bar>convertWith(foo2BarConverter).getMessage());
System.out.println(fooChild2Obj.<Bar>convertWith(foo2BarConverter).getMessage());

System.out.println();

// converting to baz
Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
System.out.println(fooObj.<Baz>convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild1Obj.<Baz>convertWith(foo2BazConverter).getMessage());
System.out.println(fooChild2Obj.<Baz>convertWith(foo2BazConverter).getMessage());

and it's output

This bar's converted from an instance of Foo
This bar's converted from an instance of FooChild1
This bar's converted from an instance of FooChild2

This baz's converted from an instance of Foo
This baz's converted from an instance of FooChild1
This baz's converted from an instance of FooChild2

At first glance looks great. But in fact this solution isn't typesafe. For example the following call

fooObj.<Baz>convertWith(foo2BarConverter).getMessage()

wouldn't cause a compile time error. But it would lead to ClassCastException in runtime.

So the general question is the following.

Is there a way to make a stateless generifyed typesafe Visitor with Java?

UPD: I've added links to sources of all three implementations: 1st, 2nd and 3rd

解决方案

Your converters are simply functions, you probably do not need a "framework" to compose them. And your third try do not make much sense:

<R> R convertWith(V converter);

mean: "given something (the V converter that know nothing about the R you want), give me anything (arbitrary R)". As you found out this does not work.

Simple Implementation using the corrected visitor pattern:

interface FooConverter<R> extends Function<Foo, R> {

  R convert(Foo convertable);

  R convert(FooChild1 convertable);

  R convert(FooChild2 convertable);

  default R apply(Foo foo) { return foo.convertWith(this); }
}

public class Foo2BarConverter implements FooConverter<Bar> {

  @Override
  public Bar convert(Foo convertable) {
    return new Bar("This bar's converted from an instance of Foo");
  }

  @Override
  public Bar convert(FooChild1 convertable) {
    return new Bar("This bar's converted from an instance of FooChild1");
  }

  @Override
  public Bar convert(FooChild2 convertable) {
    return new Bar("This bar's converted from an instance of FooChild2");
  }
}

public class Foo2BazConverter implements FooConverter<Baz> {

  @Override
  public Baz convert(Foo convertable) {
    return new Baz("This baz's converted from an instance of Foo");
  }

  @Override
  public Baz convert(FooChild1 convertable) {
    return new Baz("This baz's converted from an instance of FooChild1");
  }

  @Override
  public Baz convert(FooChild2 convertable) {
    return new Baz("This baz's converted from an instance of FooChild2");
  }
}

public class Foo{

  public <R> R convertWith(FooConverter<R> converter) {
    return converter.convert(this);
  }
}

public class FooChild1 extends Foo {

  @Override
  public <R> R convertWith(FooConverter<R>  converter) {
    return converter.convert(this);
  }
}

public class FooChild2 extends Foo {

  @Override
  public <R> R convertWith(FooConverter<R> converter) {
    return converter.convert(this);
  }
}

public void test() {
  Foo fooObj = new Foo();
  Foo fooChild1Obj = new FooChild1();
  Foo fooChild2Obj = new FooChild2();

  // converting to bar
  Foo2BarConverter foo2BarConverter = new Foo2BarConverter();
  System.out.println(fooObj.convertWith(foo2BarConverter).getMessage());
  System.out.println(fooChild1Obj.convertWith(foo2BarConverter).getMessage());
  System.out.println(fooChild2Obj.convertWith(foo2BarConverter).getMessage());

  System.out.println();

  // converting to baz
  Foo2BazConverter foo2BazConverter = new Foo2BazConverter();
  System.out.println(fooObj.convertWith(foo2BazConverter).getMessage());
  System.out.println(fooChild1Obj.convertWith(foo2BazConverter).getMessage());
  System.out.println(fooChild2Obj.convertWith(foo2BazConverter).getMessage());

  // does not compile:
  fooObj.<Baz>convertWith(foo2BarConverter).getMessage();
}

Then if you want some more framework, you may want to look into lenses: https://github.com/functionaljava/functionaljava/tree/master/core/src/main/java/fj/data/optic

这篇关于在Java中实现Visitor模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆