设计模式之访问者模式(Visitor)摘录
23種GOF設(shè)計模式一般分為三大類:創(chuàng)建型模式、結(jié)構(gòu)型模式、行為模式。
創(chuàng)建型模式抽象了實例化過程,它們幫助一個系統(tǒng)獨立于如何創(chuàng)建、組合和表示它的那些對象。一個類創(chuàng)建型模式使用繼承改變被實例化的類,而一個對象創(chuàng)建型模式將實例化委托給另一個對象。創(chuàng)建型模式有兩個不斷出現(xiàn)的主旋律。第一,它們都將關(guān)于該系統(tǒng)使用哪些具體的類的信息封裝起來。第二,它們隱藏了這些類的實例是如何被創(chuàng)建和放在一起的。整個系統(tǒng)關(guān)于這些對象所知道的是由抽象類所定義的接口。因此,創(chuàng)建型模式在什么被創(chuàng)建,誰創(chuàng)建它,它是怎樣被創(chuàng)建的,以及何時創(chuàng)建這些方面給予了很大的靈活性。它們允許用結(jié)構(gòu)和功能差別很大的“產(chǎn)品”對象配置一個系統(tǒng)。配置可以是靜態(tài)的(即在編譯時指定),也可以是動態(tài)的(在運行時)。
結(jié)構(gòu)型模式涉及到如何組合類和對象以獲得更大的結(jié)構(gòu)。結(jié)構(gòu)型類模式采用繼承機制來組合接口或?qū)崿F(xiàn)。結(jié)構(gòu)型對象模式不是對接口和實現(xiàn)進(jìn)行組合,而是描述了如何對一些對象進(jìn)行組合,從而實現(xiàn)新功能的一些方法。因為可以在運行時刻改變對象組合關(guān)系,所以對象組合方式具有更大的靈活性,而這種機制用靜態(tài)類組合是不可能實現(xiàn)的。
行為模式涉及到算法和對象間職責(zé)的分配。行為模式不僅描述對象或類的模式,還描述它們之間的通信模式。這些模式刻畫了在運行時難以跟蹤的復(fù)雜的控制流。它們將用戶的注意力從控制流轉(zhuǎn)移到對象間的聯(lián)系方式上來。行為類模式使用繼承機制在類間分派行為。行為對象模式使用對象復(fù)合而不是繼承。一些行為對象模式描述了一組對等的對象怎樣相互協(xié)作以完成其中任一個對象都無法單獨完成的任務(wù)。
創(chuàng)建型模式包括:1、FactoryMethod(工廠方法模式);2、Abstract Factory(抽象工廠模式);3、Singleton(單例模式);4、Builder(建造者模式、生成器模式);5、Prototype(原型模式).
結(jié)構(gòu)型模式包括:6、Bridge(橋接模式);7、Adapter(適配器模式);8、Decorator(裝飾模式);9、Composite(組合模式);10、Flyweight(享元模式);11、Facade(外觀模式);12、Proxy(代理模式).
行為模式包括:13、TemplateMethod(模板方法模式);14、Strategy(策略模式);15、State(狀態(tài)模式);16、Observer(觀察者模式);17、Memento(備忘錄模式);18、Mediator(中介者模式);19、Command(命令模式);20、Visitor(訪問者模式);21、Chain of Responsibility(責(zé)任鏈模式);22、Iterator(迭代器模式);23、Interpreter(解釋器模式).
Factory Method:定義一個用于創(chuàng)建對象的接口,讓子類決定將哪一個類實例化。Factory Method使一個類的實例化延遲到其子類。
Abstract Factory:提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定他們具體的類。
Singleton:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
Builder:將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
Prototype:用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這個原型來創(chuàng)建新的對象。
Bridge:將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。
Adapter:將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
Decorator:動態(tài)地給一個對象添加一些額外的職責(zé)。就擴展功能而言, Decorator模式比生成子類方式更為靈活。
Composite:將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite使得客戶對單個對象和復(fù)合對象的使用具有一致性。
Flyweight:運用共享技術(shù)有效地支持大量細(xì)粒度的對象。
Facade:為子系統(tǒng)中的一組接口提供一個一致的界面, Facade模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。
Proxy:為其他對象提供一個代理以控制對這個對象的訪問。
Template Method:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
Strategy:定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。本模式使得算法的變化可獨立于使用它的客戶。
State:允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為。對象看起來似乎修改了它所屬的類。
Observer:定義對象間的一種一對多的依賴關(guān)系,以便當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并自動刷新。
Memento:在不破壞封裝性的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)。這樣以后就可將該對象恢復(fù)到保存的狀態(tài)。
Mediator:用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
Command:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化;對請求排隊或記錄請求日志,以及支持可取消的操作。
Visitor:表示一個作用于某對象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
Chain of Responsibility:為解除請求的發(fā)送者和接收者之間耦合,而使多個對象都有機會處理這個請求。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它。
Iterator:提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內(nèi)部表示。
Interpreter:給定一個語言, 定義它的文法的一種表示,并定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子。
???????? Visitor:(1)、意圖: 表示一個作用于某對象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
???????? (2)、適用性:A、一個對象結(jié)構(gòu)包含很多類對象,它們有不同的接口,而你想對這些對象實施一些依賴于其具體類的操作。B、需要對一個對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作”污染”這些對象的類。Visitor使得你可以將相關(guān)的操作集中起來定義在一個類中。當(dāng)該對象結(jié)構(gòu)被很多應(yīng)用共享時,用Visitor模式讓每個應(yīng)用僅包含需要用到的操作。C、定義對象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。改變對象結(jié)構(gòu)類需要重定義對所有訪問者的接口,這可能需要很大的代價。如果對象結(jié)構(gòu)類經(jīng)常改變,那么可能還是在這些類中定義這些操作較好。
???????? (3)、優(yōu)缺點:A、訪問者模式使得易于增加新的操作:訪問者使得增加依賴于復(fù)雜對象結(jié)構(gòu)的構(gòu)件的操作變得容易了。僅需增加一個新的訪問者即可在一個對象結(jié)構(gòu)上定義一個新的操作。相反,如果每個功能都分散在多個類之上的話,定義新的操作時必須修改每一類。B、訪問者集中相關(guān)的操作而分離無關(guān)的操作:相關(guān)的行為不是分布在定義該對象結(jié)構(gòu)的各個類上,而是集中在一個訪問者中。無關(guān)行為卻被分別放在它們各自的訪問者子類中。這就既簡化了這些元素的類,也簡化了在這些訪問者中定義的算法。所有與它的算法相關(guān)的數(shù)據(jù)結(jié)構(gòu)都可以被隱藏在訪問者中。C、增加新的ConcreteElement類很困難:Visitor模式使得難以增加新的Element的子類。每添加一個新的ConcreteElement都要在Visitor中添加一個新的抽象操作,并在每一個ConcreteVisitor類中實現(xiàn)相應(yīng)的操作。有時可以在Visitor中提供一個缺省的實現(xiàn),這一實現(xiàn)可以被大多數(shù)的ConcreteVisitor繼承,但這與其說是一個規(guī)律還不如說是一個例外。所以在應(yīng)用訪問者模式時考慮關(guān)鍵的問題是系統(tǒng)的哪個部分會經(jīng)常變化,是作用于對象結(jié)構(gòu)上的算法呢還是構(gòu)成該結(jié)構(gòu)的各個對象的類。如果老是有新的ConcreteElement類加入進(jìn)來的話,Visitor類層次將變得難以維護(hù)。在這種情況下,直接在構(gòu)成該結(jié)構(gòu)的類中定義這些操作可能更容易一些。如果Element類層次是穩(wěn)定的,而你不斷地增加操作或修改算法,訪問者模式可以幫助你管理這些改動。D、通過類層次進(jìn)行訪問:一個迭代器可以通過調(diào)用節(jié)點對象的特定操作來遍歷整個對象結(jié)構(gòu),同時訪問這些對象。但是迭代器不能對具有不同元素類型的對象結(jié)構(gòu)進(jìn)行操作。E、累積狀態(tài):當(dāng)訪問者訪問對象結(jié)構(gòu)中的每一個元素時,它可能會累積狀態(tài)。如果沒有訪問者,這一狀態(tài)將作為額外的參數(shù)傳遞給進(jìn)行遍歷的操作,或者定義為全局變量。F、破壞封裝:訪問者無法假定ConcreteElement接口的功能足夠強,足以讓訪問者進(jìn)行它們的工作。結(jié)果是,該模式常常迫使你提供訪問元素內(nèi)部狀態(tài)的公共操作,這可能會破壞它的封裝性。
???????? (4)、相關(guān)模式:A、Composite:訪問者可以用于對一個由Composite模式定義的對象結(jié)構(gòu)進(jìn)行操作。B、Interpreter:訪問者可以用于解釋。
???????? (5)、訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)穩(wěn)定的系統(tǒng)。它把數(shù)據(jù)結(jié)構(gòu)和作用于數(shù)據(jù)結(jié)構(gòu)上的操作分離開,使得操作集合。優(yōu)點:新增加操作很容易,因為增加新操作就相當(dāng)于增加一個訪問者,訪問者模式將有關(guān)的行為集中到一個訪問者對象中。
???????? (6)、Visitor模式在不去破壞類的前提下,為類提供增加新的操作。Visitor模式的關(guān)鍵是雙分派(Double-Dispatch)的技術(shù)。C++語言支持的是單分派。雙分派意味著執(zhí)行的操作將取決于請求的種類和接收者的類型。
???????? (7)、Visitor模式可以使得Element在不修改自己的同時增加新的操作,但是這也帶來了至少以下的兩個顯著問題:A、破壞了封裝性:Visitor模式要求Visitor可以從外部修改Element對象的狀態(tài),這一般通過兩個方式來實現(xiàn)。i、Element提供足夠的public接口,使得Visitor可以通過調(diào)用這些接口達(dá)到修改Element狀態(tài)的目的;ii、Element暴露更多的細(xì)節(jié)給Visitor,或者讓Element提供public的實現(xiàn)給Visitor(當(dāng)然也給了系統(tǒng)中其它的對象),或者將Visitor聲明為Element的friend類,僅將細(xì)節(jié)暴露給Visitor。但是無論哪種情況,特別是后者都將破壞封裝性原則(實際上就是C++的friend機制得到了很多的面向?qū)ο髮<业脑嵅?。B、ConcreteElement的擴展很困難:每增加一個Element的子類,就要修改Visitor的接口,使得可以提供給這個新增加的子類的訪問機制。或者增加一個用于處理新增類的visit()接口,或者重載一個處理新增類的visit()操作,或者要修改RTTI(運行時類型識別:Runtime type identification)方式實現(xiàn)的visit()實現(xiàn)。無論哪種方式都給擴展新的Element子類帶來了困難。RTTI給接口帶來了簡單一致性,但是付出的代價是時間(RTTI的實現(xiàn))和代碼的Hard編碼(要進(jìn)行強制轉(zhuǎn)換)。
示例代碼1:
#include <iostream>
#include <string>
#include <vector>using namespace std;class Man;
class Woman;//行為
class Action
{
public:virtual void GetManConclusion(Man* concreteElementA) = 0;virtual void GetWomanConclusion(Woman* concreteElementB) = 0;
};//成功
class Success : public Action
{
public:virtual void GetManConclusion(Man* concreteElementA){cout<<"男人成功時,背后有個偉大的女人"<<endl;}virtual void GetWomanConclusion(Woman* concreteElementB){cout<<"女人成功時,背后有個沒用的男人"<<endl;}
};//失敗
class Failure : public Action
{
public:virtual void GetManConclusion(Man* concreteElementA){cout<<"男人失敗時,背后有個偉大的女人"<<endl;}virtual void GetWomanConclusion(Woman* concreteElementB){cout<<"女人失敗時,背后有個沒用的男人"<<endl;}
};//抽象人類
class Person
{
public:virtual void Accept(Action* visitor) = 0;
};//男人
class Man : public Person
{
public:virtual void Accept(Action* visitor){visitor->GetManConclusion(this);}
};//女人
class Woman : public Person
{
public:virtual void Accept(Action* visitor){visitor->GetWomanConclusion(this);}
};//對象結(jié)構(gòu)類
class ObjectStructure
{
private:vector<Person*> m_personList;
public:void Add(Person* p){m_personList.push_back(p);}void Display(Action* a){vector<Person*>::iterator p = m_personList.begin();while (p != m_personList.end()) {(*p)->Accept(a);p ++;}}
};//客戶端
int main()
{ObjectStructure* os = new ObjectStructure();os->Add(new Man());os->Add(new Woman());Success* success = new Success();os->Display(success);Failure* fl = new Failure();os->Display(fl);/*result男人成功時,背后有個偉大的女人女人成功時,背后有個沒用的男人男人失敗時,背后有個偉大的女人女人失敗時,背后有個沒用的男人*/return 0;
}
示例代碼2:
Visitor.h:
#ifndef _VISITOR_H_
#define _VISITOR_H_class ConcreteElementA;
class ConcreteElementB;
class Element;class Visitor
{
public:virtual ~Visitor();virtual void VisitConcreteElementA(Element* elm) = 0;virtual void VisitConcreteElementB(Element* elm) = 0;
protected:Visitor();
private:
};class ConcreteVisitorA : public Visitor
{
public:ConcreteVisitorA();virtual ~ConcreteVisitorA();virtual void VisitConcreteElementA(Element* elm);virtual void VisitConcreteElementB(Element* elm);
protected:
private:
};class ConcreteVisitorB : public Visitor
{
public:ConcreteVisitorB();virtual ~ConcreteVisitorB();virtual void VisitConcreteElementA(Element* elm);virtual void VisitConcreteElementB(Element* elm);
protected:
private:
};#endif//~_VISITOR_H_
Visitor.cpp:
#include "Visitor.h"
#include "Element.h"
#include <iostream>using namespace std;Visitor::Visitor()
{}Visitor::~Visitor()
{}ConcreteVisitorA::ConcreteVisitorA()
{}ConcreteVisitorA::~ConcreteVisitorA()
{}void ConcreteVisitorA::VisitConcreteElementA(Element* elm)
{cout<<"I will visit ConcreteElementA ..."<<endl;
}void ConcreteVisitorA::VisitConcreteElementB(Element* elm)
{cout<<"I will visit ConcreteElementB ..."<<endl;
}ConcreteVisitorB::ConcreteVisitorB()
{}ConcreteVisitorB::~ConcreteVisitorB()
{}void ConcreteVisitorB::VisitConcreteElementA(Element* elm)
{cout<<"I will visit ConcreteElementA ..."<<endl;
}void ConcreteVisitorB::VisitConcreteElementB(Element* elm)
{cout<<"I will visit ConcreteElementB ..."<<endl;
}
Element.h:
#ifndef _ELEMENT_H_
#define _ELEMENT_H_class Visitor;class Element
{
public:virtual ~Element();virtual void Accept(Visitor* vis) = 0;
protected:Element();
private:
};class ConcreteElementA : public Element
{
public:ConcreteElementA();~ConcreteElementA();void Accept(Visitor* vis);
protected:
private:
};class ConcreteElementB : public Element
{
public:ConcreteElementB();~ConcreteElementB();void Accept(Visitor* vis);
protected:
private:
};#endif//~_ELEMENT_H_
Element.cpp:
#include "Element.h"
#include "Visitor.h"
#include <iostream>using namespace std;Element::Element()
{}Element::~Element()
{}void Element::Accept(Visitor* vis)
{}ConcreteElementA::ConcreteElementA()
{}ConcreteElementA::~ConcreteElementA()
{}void ConcreteElementA::Accept(Visitor* vis)
{vis->VisitConcreteElementA(this);cout<<"visiting ConcreteElementA ..."<<endl;
}ConcreteElementB::ConcreteElementB()
{}ConcreteElementB::~ConcreteElementB()
{}void ConcreteElementB::Accept(Visitor* vis)
{cout<<"visiting ConcreteElementB ..."<<endl;vis->VisitConcreteElementB(this);
}
main.cpp:
#include "Element.h"
#include "Visitor.h"
#include <iostream>using namespace std;int main()
{Visitor* vis = new ConcreteVisitorA();Element* elm = new ConcreteElementA();elm->Accept(vis);/*resultI will visit ConcreteElementA ...visiting ConcreteElementA ...*/return 0;
}
訪問者模式結(jié)構(gòu)圖:
參考文獻(xiàn):
1、《大話設(shè)計模式C++》
2、《設(shè)計模式精解----GoF23種設(shè)計模式解析》
3、《設(shè)計模式----可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
總結(jié)
以上是生活随笔為你收集整理的设计模式之访问者模式(Visitor)摘录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式之命令模式(Command)摘录
- 下一篇: 设计模式之职责链模式(Chain of