访问器模式
一、动机
1.在软件构建过程中,由于需求的改变,某些层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
2.如何在不更改类层次结构的前提下载运行时候根据需要透明地为类层次结构上的各个类动态添加新的操作从而避免上述问题?
二、介绍
意图: 主要将数据结构与数据操作分离。
主要解决: 稳定的数据结构和易变的操作耦合问题。
何时使用: 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决: 在被访问的类里面加一个对外提供接待访问者的接口。
关键代码: 在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例: 您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项: 访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
三、结构
四、要点总结
1.访问器米欧式通过所谓的双重分发来实现在不更改(不添加新的操作-编译时)Element类层次结构的前提下,载运行时透明地为层次结构上的各个类动态的添加新的操作。
2.所谓的双重分发即访问器模式中间包括了两个多太分发(注意其中的多态机制):第一个为accept方法的多态解析,第二个为visitElementX方法的多态解析。
3.访问器模式最大的缺点在于扩展类的层次结构(增加新的element子类)会导致Visitor类的改变。因此访问器模式适用于Element类层次结构稳定,而其中的操作却经常改动的情况。
五、代码展示
class Visitor; class Element { public: virtual void accept(Visitor& visitor) = 0; //第一次多态辨析 virtual ~Element(){} }; class ElementA : public Element { public: void accept(Visitor &visitor) override { visitor.visitElementA(*this); } }; class ElementB : public Element { public: void accept(Visitor &visitor) override { visitor.visitElementB(*this); //第二次多态辨析 } }; class Visitor{ public: virtual void visitElementA(ElementA& element) = 0; virtual void visitElementB(ElementB& element) = 0; virtual ~Visitor(){} }; //================================== //扩展1 class Visitor1 : public Visitor{ public: void visitElementA(ElementA& element) override{ cout << "Visitor1 is processing ElementA" << endl; } void visitElementB(ElementB& element) override{ cout << "Visitor1 is processing ElementB" << endl; } }; //扩展2 class Visitor2 : public Visitor{ public: void visitElementA(ElementA& element) override{ cout << "Visitor2 is processing ElementA" << endl; } void visitElementB(ElementB& element) override{ cout << "Visitor2 is processing ElementB" << endl; } }; int main() { Visitor2 visitor; ElementB elementB; elementB.accept(visitor);// double dispatch ElementA elementA; elementA.accept(visitor); return 0; }