第28章 男人和女人——访问者模式
概念
访问者模式表示一个作用于某对象结构中的各元素的操作。它可以使你在不改变元素类的前提下,定义作用于这些元素的新操作。
即:将类的操作,变为特定对象,不同在增加新操作的时候修改类,而是增加新操作,就定义新对象,作为数据参数传递给固定类。
场景
数据结构固定的情况下(例如人类只有男人和女人),将操作从数据结构中分离出来(比如成功、失败等的反映定义成类对象传给数据实现不同操作),将数据和其操作实现了解耦,又可以方便地让操作扩展与演化。
其中,用到了双分派技术,第一次是不同者的分派(不同操作的访问类对象,传递给被操作的固定类对象),将不同的操作定义为访问者对象传给正确的被操作对象,第二次是操作中不同动作的分派(不同的固定类对象,会以不同方式使用访问者类),访问者对象中对应不同的数据对象的操作供其调用。双分派意味着执行的操作取决于请求的种类和接受者的类型。
实现
-
Visitor: 抽象访问者类,单独抽象出来表示对数据类对象的操作,其中会定义与对应固定数据子类对应的操作(如:
VisitA()
,VisitB()
) - ConcreteVisitor1,ConcreteVisitor2: 具体的访问者子类,每一个子类表示对数据类的一种操作算法,每一个子类对象会实现对应数据子类的操作。
-
Element: 包含固定子类的抽象数据类,定义一个用来接收Visitor为参数的接口(如:
accept()
),接收的Visitor便是对数据的操作。 -
ConcreteElementA,ConcreteElementB: 数据子类,实现
accept()
操作(一般就是调用Visit的对应操作),也包含一些自己的属性方法。 - ObjectStructure, 对Element对象的枚举,可以提供一个高层接口允许访问者访问它的元素,其中的
accept()
循环调用Element的accept()
。 -
Client: 如:创建ObjectStructure列表l,Element子对象e,将所有e加入l,创建Visitor子对象v,调用
l.accept(v)
以实现对所有e操作。
实现类图:
总结起来即:
- 操作抽象类–访问者类,包含 {A的操作,B的操作} 两个接口;不同操作定义不同对象类,都继承这个类形成各自的{A/B}一套操作。
- 元素抽象类–固定类,包含{接受}这一个接口;A类的{接受}调用访问者中{A的操作},B类的{接受}调用访问者中{B的操作}。
使用类图:
双分派,发生在 o.Accept(v)
调用的时候,假设o对象对应的固定类有A和B,而v对象对应了许多不同操作相关的访问者类,第一次分派就是将v传递给了正确的o(是A或者B),第二次分配就是调用到正确的v函数(是A的操作还是B的操作)。
特征
- 数据类型是稳定的不会变化(比如人只有男人和女人),这样抽象出来的操作对象里面的对应方法也是固定的不会变化,若数据变化会很麻烦。
- 支持数据的操作可变化,变化时可以定义和修改抽象出来的操作对象而不用改到具体的数据对象,体现了开放封闭原则。
举例
- 操作:失败(其中有:男人动作,女人动作)、成功(其中有男人动作,女人动作)
- 数据:男人(其中调用操作中的“男人动作”)、女人(其中调用操作中的“女人动作”)
- 使用:将操作传递给数据完成第一次分派;数据中的通用结构accept调用操作中的针对性动作。