Java中访客模式的通用实现

时间:2016-04-01 18:14:22

标签: java generics design-patterns visitor-pattern

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

我决定使用带有转换器的Visitor pattern作为访问者,将可转换类用作Elements。为了提供可扩展性和类型安全性,我决定使用泛型。因此,我所做的转换框架的第一次实施受到互联网上一些文章的影响(不幸的是我已经失去了链接)是......

使用状态转换器的转换框架

以下是框架的核心接口,Converter和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);
    }

泛型实现Convertable仅接受Converter的实现,可以转换它们并使Converter的实现仅访问Convertable的实现,并将其转换为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"); } } 。以下是此类转换器的示例:

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

以下是结果类,即Bazpublic 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; } }

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());

这是一个测试转换器的代码:

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

和此代码构建的输出

result

查看Foo2BarConverterFoo2BazConverter中的convert字段。这是实施的主要缺点。它使转换器状态良好,并不总是很方便。我试图避免这个缺点...

没有双重调度的转化框架

此实现的要点是使用结果类对转换器进行参数化,并返回Converter convertWith方法和Convertable 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

Convertable已从Converter声明中删除,因为在Convertable声明中包含结果类实际上会让我们使用结果类参数化convertWith的实现。它会将convertable的每个实现绑定到它可以转换为的唯一结果类。因此,Convertable中的Converter<A,R>指的是带有Convertable接口的接收转换器。这就是问题所在。现在调用接收转换器的convert实现将始终调用Converter接口中定义的convert,而不是Converter实现中覆盖它的convert(FooChild1 convertable)方法。换句话说,convert(FooChild2 convertable)Foo2BarConverter中的Foo2BazConverterFoo 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()); 永远不会被调用。基本上,它杀死了访客模式,双重调度的主要概念。这是一个测试代码......

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

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

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);
    }
}

下一个实现我尝试使用is ...

创建无状态转换器

使用参数化方法的转换器

这里的主要概念是仅参数化我想要返回转换结果的方法而不参数化接口的声明。

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());

测试代码

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

和它的输出

fooObj.<Baz>convertWith(foo2BarConverter).getMessage()
乍一看看起来很棒。但实际上这个解决方案并不是类型安全的。例如,以下调用

{{1}}

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

所以一般的问题如下。

有没有办法用Java创建无状态的generifyed类型安全访问者?

UPD:我添加了指向所有三种实施的来源的链接:1st2nd3rd

2 个答案:

答案 0 :(得分:1)

您的转换器只是功能,您可能不需要&#34;框架&#34;撰写它们。而你的第三次尝试没有多大意义:

<R> R convertWith(V converter);

意思是:&#34;给定的东西(V转换器对你想要的R一无所知),给我任何东西(任意R)&#34;。如你所知,这不起作用。

使用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();
}

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

答案 1 :(得分:0)

你可以放弃访客模式,因为有更好的Java解决方案。

访客模式的缺陷:

它是侵入性的,反模式的

访问者模式使用双重调度,通常如下所示:

public class ParentDataModel
{
    public void accept(Visitor visitor)
    {
        visitor.visit(this);
    }
}

public class ChildDataModel extends ParentDataModel
{
    // no need to implement accept() by the child itself
}

public class Visitor
{
    public void visit(ParentDataModel model)
    {
        // do something with it
    }

    public void visit(ChildDataModel model)
    {
        // do something with it
    }
}

为什么数据模型需要了解访问者?数据模型应该只保存与模型相关的数据。

与外部框架的现有对象不兼容

如果你需要做一些事情,比如来自J​​DK的Number,Double。

即使你说你愿意在你的项目中围绕你需要的每个对象做一个包装,但这很乏味,并且想一下你需要重构多少个类才能让它工作。

public class NumberWrapper
{
    private Number value;

    public void accept(Visitor visitor)
    {
        visitor.visit(value);
    }
}

public class DoubleWrapper
{
    private Double value;

    public void accept(Visitor visitor)
    {
        visitor.visit(value);
    }
}

public class Visitor
{
    public void visit(Number value)
    {
        // do something with it
    }

    public void visit(Double value)
    {
        // do something with it
    }
}

解决方案:一个类统治它们

public static class SuperConsumer implements Consumer
{
    private Map<Class<?>, Consumer<?>> consumers = new HashMap<>();
    private Consumer<?> unknown = o -> System.err.println("Unknown object type");

    public SuperConsumer()
    {
        consumers.put(Number.class, o -> consumeNumber(o));
        consumers.put(Double.class, o -> consumeDouble(o));
    }

    private void consumeNumber(Number value)
    {
         System.out.printf("Consuming: %s\n", value.getClass().getName());
    }

    private void consumeDouble(Double value)
    {
         System.out.printf("Consuming: %s\n", value.getClass().getName());
    }

    private Consumer findConsumer(Object object)
    {
        Consumer consumer = consumers.get(object.getClass());

        Class superClazz = object.getClass().getSuperclass();
        while (consumer == null && superClazz != Object.class)
        {
            consumer = consumers.get(superClazz);
            superClazz = superClazz.getSuperclass();
        }

        Class<?>[] interfaces = object.getClass().getInterfaces();
        for (int i = 0; consumer == null && i < interfaces.length; i++)
        {
            consumer = consumers.get(interfaces[i]);
        }

        return consumer;
    }

    @Override
    public void accept(Object object)
    {
        Consumer consumer = findConsumer(object);
        if (consumer == null)
        {
            consumer = unknown;
        }
        consumer.accept(object);
    }

    public static void main(String[] args)
    {
        Consumer consumer = new SuperConsumer();
        Arrays.asList(new Double(1.0), new Integer(1), new Float(1.0f)).forEach(o -> consumer.accept(o));
    }
}