访问者
当想要为一个对象的组合增加新的能力,且封装不重要时,就使用访问者模式。可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
典型情境
客户要求知道每道菜/原料的卡路里/蛋白质情况。此时不需要为每个菜单项和原料都添加这两个方法,只要添加一个访问者,这个访问者可以收集每道菜和原料的信息,有问题客户只要问访问者就可以了。
结构
Visitor - 为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被访问的元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
ConcreteVisitor - 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
Element - 定义一个Accept操作,它以一个访问者为参数。
ConcreteElement - 实现Accept操作,该操作以一个访问者为参数。
ObjectStructure - 能枚举它的元素。可以提供一个高层的接口以允许该访问者访问它的元素。可以是一个Composite或是一个集合,如一个列表或一个无序集合。
协作:
一个使用Visitor模式的客户必须创建一个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 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| #include <iostream> #include <list> #include <string>
class Element; class ElementA; class ElementB; class CompositeElement;
class Visitor { public: virtual void visit_elementA(ElementA* ea){} virtual void visit_elementB(ElementB* eb){} virtual void visit_composite_element(CompositeElement* ce){} };
class Element { public: virtual ~Element(){} virtual void accept(Visitor& v) = 0; };
class ElementA : public Element { public: ElementA() : Element(), m_a_num(50), m_a_string("I'm A. "){} virtual void accept(Visitor& v) { v.visit_elementA(this); } int get_a_num() {return m_a_num;} std::string get_a_string() {return m_a_string;} protected: int m_a_num; std::string m_a_string; };
class ElementB : public Element { public: ElementB() : Element(), m_b_num(30), m_b_string("I'm B. "){} virtual void accept(Visitor& v) { v.visit_elementB(this); } int get_b_num() {return m_b_num;} std::string get_b_string() {return m_b_string;} protected: int m_b_num; std::string m_b_string; };
class CompositeElement : public Element { public: void add(Element* e) {m_children.push_back(e);} void remove(Element* e) {m_children.remove(e);} virtual void accept(Visitor& v) { for (std::list<Element*>::iterator it = m_children.begin(); it != m_children.end(); ++it) { if (!(*it)) continue; (*it)->accept(v); } v.visit_composite_element(this); } int get_composite_num() { return m_children.size(); } std::string get_composite_string() { return "From All of Us. "; } protected: std::list<Element*> m_children; };
class ConcreteVisitor1 : public Visitor { public: ConcreteVisitor1() : Visitor(), m_total(""){} std::string get_total_string() {return m_total;} virtual void visit_elementA(ElementA* ea) { if (!ea) return; m_total += ea->get_a_string(); } virtual void visit_elementB(ElementB* eb) { if (!eb) return; m_total += eb->get_b_string(); } virtual void visit_composite_element(CompositeElement* ce) { if (!ce) return; m_total += ce->get_composite_string(); } protected: std::string m_total; };
class ConcreteVisitor2 : public Visitor { public: ConcreteVisitor2() : Visitor(), m_total(0) {} int get_total_num() {return m_total;} virtual void visit_elementA(ElementA* ea) { if (!ea) return; m_total += ea->get_a_num(); } virtual void visit_elementB(ElementB* eb) { if (!eb) return; m_total += eb->get_b_num(); } virtual void visit_composite_element(CompositeElement* ce) { if (!ce) return; m_total += ce->get_composite_num(); } protected: int m_total; };
class ObjectStructure { public: void test2() { Element* ea = new ElementA(); Element* eb = new ElementB(); ConcreteVisitor2 cv2; ea->accept(cv2); eb->accept(cv2); std::cout << "Get Int Sum of ElementA and ElementB: (50 + 30 = 80)" << std::endl; std::cout << cv2.get_total_num() << std::endl; } void test1() { Element* ea = new ElementA(); Element* eb = new ElementB(); CompositeElement* ce = new CompositeElement(); ce->add(ea); ce->add(eb); ConcreteVisitor1 cv1; ce->accept(cv1); std::cout << "Get String Sum of CompositeElement: (ElementA + ElementB + CompositeElement)" << std::endl; std::cout << cv1.get_total_string() << std::endl; } };
int main() { ObjectStructure o; o.test1(); std::cout << std::endl; o.test2(); system("Pause"); }
|
优点
- 允许你对组合结构加入新的操作,而无需改变结构本身。
- 想要加入新的操作,相对容易。
- 访问者所进行的操作,其代码是集中在一起的。
用途和缺点
- 当采用访问者模式的时候,会打破组合类的封装。
- 因为游走的功能牵涉其中,所以对组合结构的改变就更加困难。
相关模式
- Composite: 访问者可以用于对一个由Composite模式定义的对象结构进行操作。
- Interpreter: 访问者可以用于解释。
[1] 设计模式:可复用面向对象软件的基础
[2] Head First设计模式