访问者模式

发布时间 2023-04-05 15:48:05作者: mingmingcome

begin 2023年04月02日15:56:19

引子

悲观者往往正确,乐观者往往成功

定义

Represent an operation to be performed on the elements of an object structure.
Visitor lets you define a new operation without changing the classes of the elements
on which it operates.

表示在对象结构的元素上执行的操作。访问者模式(Visitor)允许您定义一种新操作,而无需更改它所作用的元素的类。——《设计模式:可复用面向对象软件的基础》

访问者模式是一种行为型设计模式。

使用场景

  • an object structure contains many classes of objects with differing
    interfaces, and you want to perform operations on these objects that depend
    on their concrete classes.
  • many distinct and unrelated operations need to be performed on objects in
    an object structure, and you want to avoid "polluting" their classes with
    these operations. Visitor lets you keep related operations together by
    defining them in one class. When the object structure is shared by many
    applications, use Visitor to put operations in just those applications that
    need them.
  • the classes defining the object structure rarely change, but you often want
    to define new operations over the structure. Changing the object structure
    classes requires redefining the interface to all visitors,which is
    potentially costly. If the object structure classes change often, then it's
    probably better to define the operations in those classes.
  • 对象机构包含了许多具有不同接口的对象类,您希望对这些对象执行依赖于其具体类的操作。
  • 需要对对象结构中的对象执行许多不同且不相关的操作,并且您希望避免这些操作“污染”它们的类。Visitor允许将相关操作定义在一个类中,从而将它们放在一起。当对象结构被许多应用程序共享时,使用Visitor将操作放在需要它们的应用程序中。
  • 定义对象结构的类很少改变,但是您经常希望在类上定义新的操作。更改对象结构类需要为所有访问者重新定义接口,这可能成本很高。如果对象结构类经常变化,那么最好在这些类中定义操作。

图示

访问者模式结构图:

访问者模式结构图

角色

抽象访问者角色(Visitor):

  • 为对象结构中的每个 ConcreteElement 类声明一个 visit 操作。
  • 操作的名称和签名(visitConcreteElementA、visitConcreteElementB)标识向访问者(ConcreteVisitorA、ConcreteVisitor)发送 visit 请求的类,让访问者可以确定所访问元素的具体类(ConcreteElementA、ConcreteElementB)。
  • 然后访问者可以通过元素特定的接口直接访问该元素。

具体访问者角色(ConcreteVisitorA、ConcreteVisitorB):

  • 实现访问者声明的每个操作。
  • 每个操作实现为对象结构中对应的对象类定义的算法片段(针对具体元素所执行的操作,如获取ConcreteElementA的名字等)
  • ConcreteVisitor 为算法提供了上下文,并存储其本地状态。该状态通常在遍历对象结构时积累结果。

对象结构角色(ObjectStructure):

  • 可以枚举其元素。
  • 可以提供一个高层接口,以允许访问者访问其元素。
  • 可以是一个组合(参见组合模式)或者一个集合,如列表或集合

抽象元素角色(Element):

  • 定义一个接受访问者作为参数的 Accept 操作。

具体元素角色(ConcreteElementA、ConcreteElementB):

  • 实现一个接受访问者作为参数的 Accept 操作。

代码示例

生命主要分为过去、现在、未来,乐观者看到不念过去,不畏将来,立足现在努力,悲观者看到悔恨过去,迷茫未来,或不知所措的现在,同样的生命不同的人看到不同的世界。

代码示例:

抽象访问者角色:

// 人类
public interface Man {
    void visitPast(Past past);
    void visitPresent(Present present);
    void visitFuture(Future future);
}

具体访问者角色:

// 乐观主义者
public class Optimist implements Man {
    @Override
    public void visitPast(Past past) {
        System.out.println("不念" + past.getName());
    }

    @Override
    public void visitPresent(Present present) {
        System.out.println("享受" + present.getName());
    }

    @Override
    public void visitFuture(Future future) {
        System.out.println("不畏" + future.getName());
    }
}
// 悲观主义者
public class Pessimist implements Man {
    @Override
    public void visitPast(Past past) {
        System.out.println("悔恨" + past.getName());
    }

    @Override
    public void visitPresent(Present present) {
        System.out.println("焦虑" + present.getName());
    }

    @Override
    public void visitFuture(Future future) {
        System.out.println("迷茫" + future.getName());
    }
}

对象结构角色:

// 生命
public class Life {
    private List<Time> timeList = new ArrayList<>();

    public Life() {
        timeList.add(new Past());
        timeList.add(new Present());
        timeList.add(new Future());
    }

    public void visitTime(Man man) {
        for (Time time : timeList) {
            if (time instanceof Past) {
                man.visitPast((Past) time);
            } else if (time instanceof Present) {
                man.visitPresent((Present) time);
            } else if (time instanceof Future) {
                man.visitFuture((Future) time);
            }
        }
    }
}

抽象元素角色:

// 时间
public interface Time {
    void accept(Man man);
    String getName();
}

具体元素角色:

// 过去
public class Past implements Time {

    private String name = "过去";

    @Override
    public void accept(Man man) {
        man.visitPast(this);
    }

    public String getName() {
        return name;
    }
}
// 现在
public class Present implements Time {

    private String name = "现在";

    @Override
    public void accept(Man man) {
        man.visitPresent(this);
    }
    public String getName() {
        return name;
    }
}
// 未来
public class Future implements Time {

    private String name = "未来";

    @Override
    public void accept(Man man) {
        man.visitFuture(this);
    }

    public String getName() {
        return name;
    }
}

客户端:


public class Client {
    public static void main(String[] args) {
        Optimist optimist = new Optimist();
        Pessimist pessimist = new Pessimist();

        Life life = new Life();
        System.out.println("乐观者:");
        life.visitTime(optimist);
        System.out.println("悲观者:");
        life.visitTime(pessimist);
    }
}

结果:

乐观者:
不念过去
享受现在
不畏未来
悲观者:
悔恨过去
焦虑现在
迷茫未来

优点

  • 操作和结构分开,易于新增操作

缺点

  • 修改结构需要修改所有操作类,成本极大

总结

访问者莫斯允许您定义一种新操作,而无需更改它所作用的元素的类。访问者模式适用于结构固定,但是操作不固定的对象,它把对象结构和作用于结构的操作耦合解开,方便增加新的操作。

end 2023年04月05日15:22:07