找回密码
 立即注册
首页 业界区 业界 【访问者设计模式详解】C/Java/JS/Go/Python/TS不同语言 ...

【访问者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

杜优瑗 2025-6-6 09:47:56
简介

访问者模式(Visitor Pattern)是一种行为型模式。它封装一个访问者类,把各元素类的操作集合起来,目的是将数据结构与数据操作分离。在不改变原有元素类数据结构的前提下,改变了元素类的执行算法。
当某些较为稳定的东西(数据结构或算法),不想直接被改变但又想扩展功能,这时候适合用访问者模式。访问者模式的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。
访问者模式有以下几个角色:

  • 结构对象(ObjectStructure):结构对象角色,这是访问者模式的基础角色,包含多个类或者接口.
  • 抽象元素(Element):定义一个接受访问操作accept(),以一个访问者Visitor作为参数。
  • 具体元素(ConcreteElement):实现抽象节点的accept()方法和处理操作,调用Vistor的访问方法实现具体功能。
  • 抽象访问者(Visitor):定义一个抽象接口,声明一个或多个访问操作,使得所有具体访问者都必须实现。
  • 具体访问者(ConcreteVisitor):具体访问者角色,实现Visitor声明的接口。
作用


  • 在数据基础类里面有一个方法接受访问者,将自身引用传入访问者,从而把不变的固定起来,把变化的开放出去。
  • 通过隔离类中变化的东西,固定不变的东西,符合单一职责原则,同时具备较好的扩展性和灵活性。
实现步骤


  • 先创建基本元素抽象类Element,确定accept()抽象方法。
  • 分别创建几个具体的元素类,实现抽象元素的accept方法。
  • 在创建Visitor抽象接口,定义visit方法,调用具体元素。
  • 创建1个或多个Visitor类,继承抽象接口,客户将以此去访问具体元素。
  • 再创建对象结构类,这是核心入口类,负责组合各种元素,以及传递访问者Visitor。
  • 客户调用时先创建对象结构类,再指定访问者类,通过访问这类调用具体元素类
UML

1.png

 
Java代码

结构对象
  1. // ObjectStructure.java 结构对象(ObjectStructure)
  2. public class ObjectStructure {
  3.   // 可以想象为一台电脑,聚合了各种设备元素
  4.   private String name = "Computer Structure";
  5.   private List<Element> elements = new ArrayList<Element>();
  6.   // 结构对象初始化聚合了其他元素
  7.   public ObjectStructure() {
  8.     addElement(new ConcreteElementA());
  9.     addElement(new ConcreteElementB());
  10.   }
  11.   public void addElement(Element element) {
  12.     elements.add(element);
  13.   }
  14.   // 传入访问者分发给其他元素
  15.   public void accept(Visitor visitor) {
  16.     System.out
  17.         .println("ObjectStructure::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
  18.             + visitor.getName() + "]");
  19.     for (Element element : elements) {
  20.       element.accept(visitor);
  21.     }
  22.   }
  23.   public String getName() {
  24.     return this.name;
  25.   }
  26. }
复制代码
抽象访问者类
  1. // Visitor.java 访问者Visitor抽象接口,定义不同的visit方法
  2. public interface Visitor {
  3.   public void visit(ConcreteElementA concreteElementA);
  4.   public void visit(ConcreteElementB concreteElementB);
  5.   public String getName();
  6. }
复制代码
具体访问者
  1. // ConcreteVisitorA.java 具体访问者A
  2. public class ConcreteVisitorA implements Visitor {
  3.   // 假如由不同厂商是程序的访问者
  4.   private String name = "Google Visitor";
  5.   @Override
  6.   public void visit(ConcreteElementA concreteElementA) {
  7.     System.out.println(
  8.         "ConcreteVisitorA::visit() [Element.class = " + concreteElementA.getClass().getSimpleName()
  9.             + " Element.name = "
  10.             + concreteElementA.getName() + "]");
  11.     concreteElementA.operate();
  12.   }
  13.   @Override
  14.   public void visit(ConcreteElementB concreteElementB) {
  15.     System.out.println("ConcreteVisitorA::visit() [Element.class = " + concreteElementB.getClass().getSimpleName()
  16.         + " Element.name = "
  17.         + concreteElementB.getName() + "]");
  18.     concreteElementB.operate();
  19.   }
  20.   public String getName() {
  21.     return this.name;
  22.   }
  23. }
复制代码
  1. // ConcreteVisitorB.java 具体访问者B
  2. public class ConcreteVisitorB implements Visitor {
  3.   // 假如由不同厂商是程序的访问者
  4.   private String name = "Apple Visitor";
  5.   @Override
  6.   public void visit(ConcreteElementA concreteElementA) {
  7.     System.out.println(
  8.         "ConcreteVisitorB::visit() [Element.class = " + concreteElementA.getClass().getSimpleName()
  9.             + " Element.name = "
  10.             + concreteElementA.getName() + "]");
  11.     concreteElementA.operate();
  12.   }
  13.   @Override
  14.   public void visit(ConcreteElementB concreteElementB) {
  15.     System.out.println(
  16.         "ConcreteVisitorB::visit() [Element.class = " + concreteElementB.getClass().getSimpleName()
  17.             + " Element.name = "
  18.             + concreteElementB.getName() + "]");
  19.     concreteElementB.operate();
  20.   }
  21.   public String getName() {
  22.     return this.name;
  23.   }
  24. }
复制代码
抽象元素类
  1. // Element.java 抽象元素(Element),定义accept方法,传入抽象访问者
  2. abstract class Element {
  3.   public abstract void accept(Visitor visitor);
  4. }
复制代码
具体元素实现类
  1. // ConcreteElementA.java 具体的元素实现者A
  2. public class ConcreteElementA extends Element {
  3.   // 可以设想为显示器
  4.   private String name = "Monitor Element";
  5.   @Override
  6.   public void accept(Visitor visitor) {
  7.     System.out
  8.         .println(
  9.             "ConcreteElementA::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
  10.                 + visitor.getName() + "]");
  11.     visitor.visit(this);
  12.   }
  13.   public void operate() {
  14.     System.out.println("ConcreteElementA::operate() [" + this.getName() + "]");
  15.   }
  16.   public String getName() {
  17.     return this.name;
  18.   }
  19. }
复制代码
  1. // ConcreteElementB.java 具体的元素实现者B
  2. public class ConcreteElementB extends Element {
  3.   private String name = "Keyboard Element";
  4.   @Override
  5.   public void accept(Visitor visitor) {
  6.     System.out.println(
  7.         "ConcreteElementB::accept() [visitor.class = " + visitor.getClass().getSimpleName() + " visitor.name = "
  8.             + visitor.getName() + "]");
  9.     visitor.visit(this);
  10.   }
  11.   public void operate() {
  12.     System.out.println("ConcreteElementB::operate() [" + this.getName() + "]");
  13.   }
  14.   public String getName() {
  15.     return this.name;
  16.   }
  17. }
复制代码
测试调用
  1.   /**
  2.    * 访问者模式是当客户需要访问具体各元素Element时,先建立一个访问者Visitor作为媒介
  3.    * 客户基于对象结构ObjectStructure,调用accept(),接受传入的访问者
  4.    * 对象结构向其他元素负责分发访问者,元素对象接受之后会将自己回传给访问者,从而访问者可以访问具体元素
  5.    */
  6.     ObjectStructure structure = new ObjectStructure();
  7.     // 接受访问者A,把访问者传递给具体元素
  8.     structure.accept(new ConcreteVisitorA());
  9.     System.out.println("====");
  10.     // 接受访问者B,把访问者传递给具体元素
  11.     structure.accept(new ConcreteVisitorB());
复制代码
Go代码

结构对象
  1. // ObjectStructure.go 结构对象(ObjectStructure)
  2. type ObjectStructure struct {
  3.   name     string
  4.   elements []Element
  5. }
  6. func (o *ObjectStructure) AddElement(e Element) {
  7.   o.elements = append(o.elements, e)
  8. }
  9. // 传入访问者分发给其他元素
  10. func (o *ObjectStructure) Accept(v Visitor) {
  11.   fmt.Println(
  12.     "ObjectStructure::Accept() [Visitor.name = " +
  13.       v.GetName() + "]")
  14.   // 通知全部元素成员接受访问者
  15.   for i := 0; i < len(o.elements); i++ {
  16.     o.elements[i].Accept(v)
  17.   }
  18.   // for _, ele := range o.elements {
  19.   //   ele.Accept(v)
  20.   // }
  21. }
  22. func (o *ObjectStructure) GetName() string {
  23.   o.name = "Computer Structure"
  24.   return o.name
  25. }
  26. // 结构对象的初始化函数
  27. func (o *ObjectStructure) Init() {
  28.   // 可以想象为一台电脑,聚合了各种设备元素
  29.   fmt.Println("ObjectStructure::Init() ", o.GetName())
  30.   // 定义一个对象数组,长度可选
  31.   o.elements = make([]Element, 0, 100)
  32.   // 结构对象初始化聚合了其他元素
  33.   o.AddElement(&ConcreteElementA{})
  34.   o.AddElement(&ConcreteElementB{})
  35. }
复制代码
抽象访问者类
  1. // Visitor.go 访问者Visitor抽象接口,定义不同的visit方法
  2. type Visitor interface {
  3.   VisitA(e *ConcreteElementA)
  4.   VisitB(e *ConcreteElementB)
  5.   GetName() string
  6. }
复制代码
具体访问者
  1. // ConcreteVisitorA.go 具体访问者A
  2. type ConcreteVisitorA struct {
  3.   name string
  4. }
  5. func (v *ConcreteVisitorA) GetName() string {
  6.   v.name = "Google Visitor(struct=ConcreteVisitorA)"
  7.   return v.name
  8. }
  9. func (v *ConcreteVisitorA) VisitA(e *ConcreteElementA) {
  10.   fmt.Println(
  11.     "ConcreteVisitorA::VisitA() [Element.name = " + e.GetName() + "]")
  12.   e.Operate()
  13. }
  14. func (v *ConcreteVisitorA) VisitB(e *ConcreteElementB) {
  15.   fmt.Println(
  16.     "ConcreteVisitorA::VisitB() [Element.name = " + e.GetName() + "]")
  17.   e.Operate()
  18. }
复制代码
  1. // ConcreteVisitorB.go 具体访问者B
  2. type ConcreteVisitorB struct {
  3.   name string
  4. }
  5. func (v *ConcreteVisitorB) GetName() string {
  6.   v.name = "Apple Visitor(struct=ConcreteVisitorB)"
  7.   return v.name
  8. }
  9. func (v *ConcreteVisitorB) VisitB(e *ConcreteElementB) {
  10.   fmt.Println(
  11.     "ConcreteVisitorB::VisitB() [Element.name = " + e.GetName() + "]")
  12.   e.Operate()
  13. }
  14. func (v *ConcreteVisitorB) VisitA(e *ConcreteElementA) {
  15.   fmt.Println(
  16.     "ConcreteVisitorB::VisitA() [Element.name = " + e.GetName() + "]")
  17.   e.Operate()
  18. }
复制代码
抽象元素类
  1. // Element.go 抽象元素(Element),定义accept方法,传入抽象访问者
  2. // go无抽象类,用interface替代
  3. type Element interface {
  4.   Accept(v Visitor)
  5.   Operate()
  6.   GetName() string
  7. }
复制代码
具体元素实现类
  1. // ConcreteElementA.go 具体的元素实现者A
  2. type ConcreteElementA struct {
  3.   name string
  4. }
  5. func (c *ConcreteElementA) GetName() string {
  6.   c.name = `Monitor Element(struct=ConcreteElementA)`
  7.   return c.name
  8. }
  9. func (e *ConcreteElementA) Accept(v Visitor) {
  10.   fmt.Println(
  11.     "ConcreteElementA::Accept() [Visitor.name = " + v.GetName() + "]")
  12.   v.VisitA(e)
  13. }
  14. func (e *ConcreteElementA) Operate() {
  15.   fmt.Println("ConcreteElementA::Operate() [" + e.GetName() + "]")
  16. }
复制代码
  1. // ConcreteElementB.go 具体的元素实现者B
  2. type ConcreteElementB struct {
  3.   name string
  4. }
  5. func (c *ConcreteElementB) GetName() string {
  6.   c.name = "Keyboard Element(struct=ConcreteElementB)"
  7.   return c.name
  8. }
  9. func (e *ConcreteElementB) Accept(v Visitor) {
  10.   fmt.Println(
  11.     "ConcreteElementB::Accept() [Visitor.name = " + v.GetName() + "]")
  12.   v.VisitB(e)
  13. }
  14. func (e *ConcreteElementB) Operate() {
  15.   fmt.Println("ConcreteElementB::Operate() [" + e.GetName() + "]")
  16. }
复制代码
测试调用
  1. func main() {
  2.   fmt.Println("test start:")
  3.   /**
  4.    * 访问者模式是当客户需要访问具体各元素Element时,先建立一个访问者Visitor作为媒介
  5.    * 客户基于对象结构ObjectStructure,调用Accept(),接受传入的访问者
  6.    * 对象结构向其他元素负责分发访问者,元素对象接受之后会将自己回传给访问者,从而访问者可以访问具体元素
  7.    */
  8.   structure := src.ObjectStructure{}
  9.   structure.Init()
  10.   // 接受访问者A,把访问者传递给具体元素
  11.   structure.Accept(&src.ConcreteVisitorA{})
  12.   fmt.Println("====")
  13.   // 接受访问者B,把访问者传递给具体元素
  14.   structure.Accept(&src.ConcreteVisitorB{})
  15. }
复制代码
更多语言版本

不同语言设计模式源码:https://github.com/microwind/design-pattern

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册