考虑 Visitor_pattern 的原因之一:
A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.
假设您没有第三方库的源代码,并且您在相关对象上添加了一个操作。
由于您没有对象,因此无法修改您的元素(第三方类)以添加访客。
在这种情况下,双重分派(dispatch)是不可能的。
那么通常首选哪个选项?
选项 1:
在第三方类之上再扩展一个继承层次结构并实现如图所示的双重分派(dispatch)模式?
对于扩展 A 类的给定 B 类层次结构,我将添加
ElementA extends A
ElementB extends B
现在 ConcreteElements 是从 ElementA 而不是类 A 派生的。
缺点:类的数量会增加。
选项 2:
使用 Visitor 类作为中央帮助类,并通过一次分派(dispatch)完成工作。
缺点:我们并没有按照 UML 图真正地遵循访问者模式。
如果我错了,请更正。
最佳答案
您可以结合使用Wrapper 和Visitor 来解决您的问题。
使用包装器添加 visit
方法可以提高这些对象的可用性。当然,您可以获得包装器的全部优点(对遗留类的依赖性较低)和缺点(附加对象)。
这是一个用 JAVA 编写的示例(因为它非常严格,不会自己进行双重分派(dispatch),而且我对它非常熟悉):
1) 您的遗留对象
假设您有遗留对象 Legacy1
和 Legacy2
,您无法更改它们,它们具有特定的业务方法:
public final class Legacy1 {
public void someBusinessMethod1(){
...
}
}
和
public final class Legacy2 {
public void anotherBusinessMethod(){
...
}
}
2) 准备包装
你只需将它们包装在一个 VisitableWrapper 中,它有一个 visit
方法来获取你的 visitor,例如:
public interface VisitableWrapper {
public void accept(Visitor visitor);
}
具有以下实现:
public class Legacy1Wrapper implements VisitableWrapper {
private final Legacy1 legacyObj;
public Legacy1Wrapper(Legacy1 original){
this.legacyObj = original;
}
public void accept(Visitor visitor){
visitor.visit(legacyObj);
}
}
和
public class Legacy2Wrapper implements VisitableWrapper {
private final Legacy2 legacyObj;
public Legacy2Wrapper(Legacy2 original){
this.legacyObj = original;
}
public void accept(Visitor visitor){
visitor.visit(legacyObj);
}
}
3) 访客,准备就绪!
然后你自己的 Visitor 可以像这样设置来访问包装器:
public interface Visitor {
public void visit(Legacy1 leg);
public void visit(Legacy2 leg);
}
像这样的实现:
public class SomeLegacyVisitor{
public void visit(Legacy1 leg){
System.out.println("This is a Legacy1! let's do something with it!");
leg.someBusinessMethod1();
}
public void visit(Legacy2 leg){
System.out.println("Hum, this is a Legacy 2 object. Well, let's do something else.");
leg.anotherBusinessMethod();
}
}
4) 释放能量
最后在您的代码中,该框架将像这样工作:
public class TestClass{
// Start off with some legacy objects
Legacy1 leg1 = ...
Legacy2 leg2 = ...
// Wrap all your legacy objects into a List:
List<VisitableWrapper> visitableLegacys = new ArrayList<>();
visitableLegacys.add(new Legacy1Wrapper(legacy1));
visitableLegacys.add(new Legacy2Wrapper(legacy2));
// Use any of your visitor implementations!
Visitor visitor = new SomeLegacyVisitor();
for(VisitableWrapper wrappedLegacy: visitableLegacys){
wrappedLegacy.accept(visitor);
}
}
预期输出:
This is a Legacy1! let's do something with it!
Hum, this is a Legacy 2 object. Well, let's do something else.
缺点:
- 相当多的样板文件。使用 Lombok如果您使用 Java 进行开发。
- 相当多的包装对象实例。对您来说可能是问题,也可能不是问题。
- 你需要事先知道对象的具体类型。这意味着您知道它们的子类型,它们不是列表中的 bundle 。如果是这种情况,您别无选择,只能使用反射。
关于java - 在源代码不可用的情况下实现访问者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38920520/