设计模式之访问者模式
意图
访问者模式(Visitor) 是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。
适用场景
- 如果你需要对一个复杂对象结构 (例如对象树) 中的所有元素执行某些操作, 可使用访问者模式。
- 可使用访问者模式来清理辅助行为的业务逻辑。
- 当某个行为仅在类层次结构中的一些类中有意义, 而在其他类中没有意义时, 可使用该模式。
结构
结构说明
- 访问者 (Visitor) 接口声明了一系列以对象结构的具体元素为参数的访问者方法。 如果编程语言支持重载, 这些方法的名称可以是相同的, 但是其参数一定是不同的。
- 具体访问者 (Concrete Visitor) 会为不同的具体元素类实现相同行为的几个不同版本。
- 元素 (Element) 接口声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。
- 具体元素 (Concrete Element) 必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。 请注意, 即使元素基类实现了该方法, 所有子类都必须对其进行重写并调用访问者对象中的合适方法。
- 客户端 (Client) 通常会作为集合或其他复杂对象 (例如一个组合树) 的代表。 客户端通常不知晓所有的具体元素类, 因为它们会通过抽象接口与集合中的对象进行交互。
结构代码范式
Visitor : 为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。
1 2 3 4
| abstract class Visitor { public abstract void VisitConcreteElementA(ConcreteElementA elementA); public abstract void VisitConcreteElementB(ConcreteElementB elementB); }
|
ConcreteVisitor : 实现每个由 Visitor 声明的操作。每个操作实现算法的一部分,而该算法片段乃是对应于结构中对象的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class ConcreteVisitor1 extends Visitor { @Override public void VisitConcreteElementA(ConcreteElementA elementA) { System.out.println(this.getClass().getName() + " 访问 " + elementA.getClass().getName()); }
@Override public void VisitConcreteElementB(ConcreteElementB elementB) { System.out.println(this.getClass().getName() + " 访问 " + elementB.getClass().getName()); } }
class ConcreteVisitor2 extends Visitor { @Override public void VisitConcreteElementA(ConcreteElementA elementA) { System.out.println(this.getClass().getName() + " 访问 " + elementA.getClass().getName()); }
@Override public void VisitConcreteElementB(ConcreteElementB elementB) { System.out.println(this.getClass().getName() + " 访问 " + elementB.getClass().getName()); } }
|
Element : 定义一个 Accpet 操作,它以一个访问者为参数。
1 2 3
| abstract class Element { public abstract void Accept(Visitor visitor); }
|
ConcreteElement : 实现 Element 声明的 Accept 操作。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class ConcreteElementA extends Element { @Override public void Accept(Visitor visitor) { visitor.VisitConcreteElementA(this); } }
class ConcreteElementB extends Element { @Override public void Accept(Visitor visitor) { visitor.VisitConcreteElementB(this); } }
|
ObjectStructure : 可以枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class ObjectStructure { private List<Element> elements = new ArrayList<Element>();
public void Attach(Element element) { elements.add(element); }
public void Detach(Element element) { elements.remove(element); }
public void Accept(Visitor visitor) { for (Element elem : elements) { elem.Accept(visitor); } } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class VisitorPattern { public static void main(String[] args) { ObjectStructure o = new ObjectStructure(); o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1(); ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.Accept(v1); o.Accept(v2); } }
|
输出
1 2 3 4
| ConcreteVisitor1 访问 ConcreteElementA ConcreteVisitor1 访问 ConcreteElementB ConcreteVisitor2 访问 ConcreteElementA ConcreteVisitor2 访问 ConcreteElementB
|
伪代码
在本例中, 访问者模式为几何图像层次结构添加了对于 XML 文件导出功能的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
interface Shape is method move(x, y) method draw() method accept(v: Visitor)
class Dot implements Shape is
method accept(v: Visitor) is v.visitDot(this)
class Circle implements Shape is method accept(v: Visitor) is v.visitCircle(this)
class Rectangle implements Shape is method accept(v: Visitor) is v.visitRectangle(this)
class CompoundShape implements Shape is method accept(v: Visitor) is v.visitCompoundShape(this)
interface Visitor is method visitDot(d: Dot) method visitCircle(c: Circle) method visitRectangle(r: Rectangle) method visitCompoundShape(cs: CompoundShape)
class XMLExportVisitor implements Visitor is method visitDot(d: Dot) is
method visitCircle(c: Circle) is
method visitRectangle(r: Rectangle) is
method visitCompoundShape(cs: CompoundShape) is
class Application is field allShapes: array of Shapes
method export() is exportVisitor = new XMLExportVisitor()
foreach (shape in allShapes) do shape.accept(exportVisitor)
|
案例
使用示例: 访问者不是常用的设计模式, 因为它不仅复杂, 应用范围也比较狭窄。
这里是 Java 程序库代码中该模式的一些示例:
与其他模式的关系
参考资料