在源代码不可用的情况下实现访问者模式

Adi*_*a W 8 java design-patterns visitor-pattern

考虑Visitor_pattern的原因之一:

这种分离的实际结果是能够在不修改这些结构的情况下向现有对象结构添加新操作.

假设您没有第三方库的源代码,并且已在相关对象上添加了一个操作.

由于您没有对象,因此无法修改您的元素(第三方类)以添加访问者.

在此输入图像描述

在这种情况下,双重调度是不可能的.

那么哪种选择通常是首选?

Option 1: 在第三方类之上扩展一个继承层次结构并实现模式如图所示双重调度?

对于扩展A类的给定B类层次结构,我将添加

ElementA extends A
ElementB extends B
Run Code Online (Sandbox Code Playgroud)

现在,ConcreteElements派生自ElementA而不是A类.

缺点:课程数量会增加.

Option 2: 使用Visitor类作为中心帮助程序类,并使用单个调度完成工作.

缺点:根据UML图,我们并没有真正关注Visitor模式.

如果我错了,请纠正.

小智 11

您可以将Wrapper和 Visitor结合起来解决您的问题.使用包装上,以添加一个visit方法,可以增加这些对象的可用性.当然,你可以获得包装器的全部优点(对遗留类的依赖性较小)和缺点(附加对象).


这是JAVA中一个经过深思熟虑的例子(因为它非常严格,不会单独进行双重调度,而且我对它非常熟悉):

1)您的旧对象

假设你有你的旧对象Legacy1和Legacy2你无法改变,这有具体的业务方法:

public final class Legacy1 {
    public void someBusinessMethod1(){
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

和

public final class Legacy2 {
    public void anotherBusinessMethod(){
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

2)准备包装器

你只需将它们包装在一个VisitableWrapper中,它有一个visit方法可以吸引访问者,例如:

public interface VisitableWrapper {
    public void accept(Visitor visitor);
}
Run Code Online (Sandbox Code Playgroud)

通过以下实现:

public class Legacy1Wrapper implements VisitableWrapper {

    private final Legacy1 legacyObj;

    public Legacy1Wrapper(Legacy1 original){
        this.legacyObj = original;
    }

    public void accept(Visitor visitor){
         visitor.visit(legacyObj);
    }
}
Run Code Online (Sandbox Code Playgroud)

和

public class Legacy2Wrapper implements VisitableWrapper {

    private final Legacy2 legacyObj;

    public Legacy2Wrapper(Legacy2 original){
        this.legacyObj = original;
    }

    public void accept(Visitor visitor){
         visitor.visit(legacyObj);
    }
}
Run Code Online (Sandbox Code Playgroud)

3)访客,准备好了!

然后你可以设置你自己的访问者访问包装器,如下所示:

public interface Visitor {
     public void visit(Legacy1 leg);
     public void visit(Legacy2 leg);
}
Run Code Online (Sandbox Code Playgroud)

有这样的实现:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

This is a Legacy1! let's do something with it!
Hum, this is a Legacy 2 object. Well, let's do something else.
Run Code Online (Sandbox Code Playgroud)

缺点:

  1. 相当多的样板.如果使用Java开发,请使用Lombok.
  2. 相当多的包装器对象实例.可能或可能不是你的问题.
  3. 您需要事先知道对象的具体类型.这意味着您知道它们的子类型,它们不是List中的包.如果是这种情况,除了使用反射之外别无选择.