【发布时间】:2016-04-01 18:14:21
【问题描述】:
我进行了一些研究,试图开发一个类型转换框架,该框架提供将源类(例如,Foo)的实例转换为结果类(例如,Bar 或 Baz)的实例的能力。该框架应该能够为同一对源和结果使用不同的转换逻辑(即不同的转换器)。它还应该是可扩展的,即允许为新的和现有的源和结果对添加新的转换器。另一个要求是类型安全,即任何尝试将某个源类的实例转换为结果类的实例而没有转换器实现适当的转换逻辑的任何尝试都应该导致编译时错误。
我决定使用Visitor pattern,将转换器作为访客,将可转换类作为元素。为了提供可扩展性和类型安全性,我决定使用泛型。所以我做的转换框架的第一个实现是受到互联网上一些文章的影响(不幸的是我失去了链接)是......
带有全状态转换器的转换框架
以下是框架的核心接口,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 和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;
}
}
这是一个测试转换器的代码:
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
查看Foo2BarConverter 和Foo2BazConverter 中的result 字段。这是实施的主要缺点。它使转换器有状态,这并不总是很方便。试图避免我开发的这个缺点......
没有双重调度的转换框架
此实现的要点是使用结果类对转换器进行参数化,并从Converter 的convert 方法和Convertable 的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> 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 指的是带有Converter<A,R> 接口的接收转换器。这就是问题所在。现在调用接收转换器的Convertable 的实现将始终调用convert,它在Converter 接口中定义,而不是在convert 实现中覆盖它的convert 方法。换句话说,Foo2BarConverter 和 Foo2BazConverter 中的 convert(FooChild1 convertable) 和 convert(FooChild2 convertable) 永远不会被调用。基本上,它扼杀了访问者模式的主要概念,即双重调度。这是一个测试代码...
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());
及其输出表明在此实现中未调用覆盖方法。
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
下一个实现我尝试用 was... 制作无状态转换器。
带有参数化方法的转换器
这里的主要概念是仅对我想要返回转换结果的方法进行参数化,而不对接口声明进行参数化。
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);
}
}
测试代码
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()
不会导致编译时错误。但这会导致运行时出现 ClassCastException。
所以一般问题如下。
有没有办法用 Java 制作无状态的泛型类型安全访问者?
【问题讨论】:
标签: java generics design-patterns visitor-pattern