C++設(shè)計(jì)模式之訪問(wèn)者模式
前言
這是23+1(簡(jiǎn)單工廠模式)之中的最后一個(gè)了——訪問(wèn)者模式。訪問(wèn)者模式也是一個(gè)比較麻煩的設(shè)計(jì)模式。我也沒(méi)有實(shí)戰(zhàn)經(jīng)驗(yàn),對(duì)于訪問(wèn)者模式的理解完全來(lái)自GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,而這篇文章就是根據(jù)對(duì)這本書(shū)的理解而寫(xiě)出來(lái)的。在讀《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》的時(shí)候,讓我想起自己做過(guò)的一個(gè)項(xiàng)目,該項(xiàng)目雖然沒(méi)有使用訪問(wèn)者模式,但是,今天理解了該模式,如果使用該模式對(duì)之前做過(guò)的項(xiàng)目進(jìn)行重構(gòu),將是一個(gè)不錯(cuò)的想法。
訪問(wèn)者模式
在GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(shū)中對(duì)訪問(wèn)者模式是這樣說(shuō)的:表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。訪問(wèn)者模式把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開(kāi),使得操作集合可以相對(duì)自由地演化。該模式的目的是要把處理從數(shù)據(jù)結(jié)構(gòu)分離出來(lái)。訪問(wèn)者模式讓增加新的操作很容易,因?yàn)樵黾有碌牟僮骶鸵馕吨黾右粋€(gè)新的訪問(wèn)者。訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中?,F(xiàn)在再來(lái)說(shuō)說(shuō)我之前經(jīng)歷過(guò)的那個(gè)項(xiàng)目。
是基于Windows Shell開(kāi)發(fā)的一個(gè)項(xiàng)目,在一個(gè)容器中存儲(chǔ)了很多的Shell Items,同時(shí)定義了對(duì)Items的操作,由于項(xiàng)目一直都在進(jìn)行后期擴(kuò)展,對(duì)Items的操作在后期都需要進(jìn)行擴(kuò)展的;而現(xiàn)在的做法是,定義一個(gè)操作類,該操作類中定義了一個(gè)集合,該集合存放Items,在該操作類中擴(kuò)展對(duì)應(yīng)的操作方法。現(xiàn)在想想如果使用訪問(wèn)者模式也是可以的,由于Items集合是固定的,當(dāng)需要擴(kuò)展集合的操作時(shí),只需要添加對(duì)應(yīng)的訪問(wèn)者即可。
UML類圖
Visitor(訪問(wèn)者):為該對(duì)象結(jié)構(gòu)中ConcreteElement的每一個(gè)類聲明一個(gè)Visit操作。該操作的名字和特征標(biāo)識(shí)了發(fā)送Visit請(qǐng)求給該訪問(wèn)者的那個(gè)類。這使得訪問(wèn)者可以確定正被訪問(wèn)元素的具體的類。這樣訪問(wèn)者就可以通過(guò)該元素的特定接口直接訪問(wèn)它。
ConcreteVisitor(具體訪問(wèn)者):實(shí)現(xiàn)每個(gè)由Visitor聲明的操作。每個(gè)操作實(shí)現(xiàn)本算法的一部分,而該算法片段乃是對(duì)應(yīng)于結(jié)構(gòu)中對(duì)象的類。ConcreteVisitor為該算法提供了上下文并存儲(chǔ)它的局部狀態(tài)。這一狀態(tài)常常在遍歷該結(jié)構(gòu)的過(guò)程中累積結(jié)果。
Element(元素):定義一個(gè)Accept操作,它以一個(gè)訪問(wèn)者為參數(shù)。
ConcreteElement(具體元素):實(shí)現(xiàn)Accept操作,該操作以一個(gè)訪問(wèn)者為參數(shù)。
ObjectStructure(對(duì)象結(jié)構(gòu)):能夠枚舉它的元素,同時(shí)提供一個(gè)高層的接口以允許該訪問(wèn)者訪問(wèn)它的元素。
使用場(chǎng)合
1.一個(gè)對(duì)象結(jié)構(gòu)包含很多類對(duì)象,它們有不同的接口,而你想對(duì)這些對(duì)象實(shí)施一些依賴于其具體類的操作;
2.需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作“污染”這些對(duì)象的類。Visitor使得你可以將相關(guān)的操作集中起來(lái)定義在一個(gè)類中;
3.當(dāng)該對(duì)象結(jié)構(gòu)被很多應(yīng)用共享時(shí),用Visitor模式讓每個(gè)應(yīng)用僅包含需要用到的操作;
4.定義對(duì)象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。改變對(duì)象結(jié)構(gòu)類需要重定義對(duì)所有訪問(wèn)者的接口,這可能需要很大的代價(jià)。如果對(duì)象結(jié)構(gòu)類經(jīng)常改變,那么可能還是在這些類中定義這些操作較好。
代碼實(shí)現(xiàn)
#include <iostream>
#include <vector>
using namespace std;
class ConcreteElementA;
class ConcreteElementB;
class Visitor
{
public:
virtual void VisitConcreteElementA(ConcreteElementA *pElementA) = 0;
virtual void VisitConcreteElementB(ConcreteElementB *pElementB) = 0;
};
class ConcreteVisitor1 : public Visitor
{
public:
void VisitConcreteElementA(ConcreteElementA *pElementA);
void VisitConcreteElementB(ConcreteElementB *pElementB);
};
void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA *pElementA)
{
// 現(xiàn)在根據(jù)傳進(jìn)來(lái)的pElementA,可以對(duì)ConcreteElementA中的element進(jìn)行操作
}
void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB *pElementB)
{
// 現(xiàn)在根據(jù)傳進(jìn)來(lái)的pElementB,可以對(duì)ConcreteElementB中的element進(jìn)行操作
}
class ConcreteVisitor2 : public Visitor
{
public:
void VisitConcreteElementA(ConcreteElementA *pElementA);
void VisitConcreteElementB(ConcreteElementB *pElementB);
};
void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA *pElementA)
{
// ...
}
void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB *pElementB)
{
// ...
}
// Element object
class Element
{
public:
virtual void Accept(Visitor *pVisitor) = 0;
};
class ConcreteElementA : public Element
{
public:
void Accept(Visitor *pVisitor);
};
void ConcreteElementA::Accept(Visitor *pVisitor)
{
pVisitor->VisitConcreteElementA(this);
}
class ConcreteElementB : public Element
{
public:
void Accept(Visitor *pVisitor);
};
void ConcreteElementB::Accept(Visitor *pVisitor)
{
pVisitor->VisitConcreteElementB(this);
}
// ObjectStructure類,能枚舉它的元素,可以提供一個(gè)高層的接口以允許訪問(wèn)者訪問(wèn)它的元素
class ObjectStructure
{
public:
void Attach(Element *pElement);
void Detach(Element *pElement);
void Accept(Visitor *pVisitor);
private:
vector<Element *> elements;
};
void ObjectStructure::Attach(Element *pElement)
{
elements.push_back(pElement);
}
void ObjectStructure::Detach(Element *pElement)
{
vector<Element *>::iterator it = find(elements.begin(), elements.end(), pElement);
if (it != elements.end())
{
elements.erase(it);
}
}
void ObjectStructure::Accept(Visitor *pVisitor)
{
// 為每一個(gè)element設(shè)置visitor,進(jìn)行對(duì)應(yīng)的操作
for (vector<Element *>::const_iterator it = elements.begin(); it != elements.end(); ++it)
{
(*it)->Accept(pVisitor);
}
}
int main()
{
ObjectStructure *pObject = new ObjectStructure;
ConcreteElementA *pElementA = new ConcreteElementA;
ConcreteElementB *pElementB = new ConcreteElementB;
pObject->Attach(pElementA);
pObject->Attach(pElementB);
ConcreteVisitor1 *pVisitor1 = new ConcreteVisitor1;
ConcreteVisitor2 *pVisitor2 = new ConcreteVisitor2;
pObject->Accept(pVisitor1);
pObject->Accept(pVisitor2);
if (pVisitor2) delete pVisitor2;
if (pVisitor1) delete pVisitor1;
if (pElementB) delete pElementB;
if (pElementA) delete pElementA;
if (pObject) delete pObject;
return 0;
}
總結(jié)
訪問(wèn)者模式的基本思想如下:首先擁有一個(gè)由許多對(duì)象構(gòu)成的對(duì)象結(jié)構(gòu),就是上面代碼中的ObjectStructure,這些對(duì)象的類都擁有一個(gè)Accept方法用來(lái)接受訪問(wèn)者對(duì)象;訪問(wèn)者是一個(gè)接口,它擁有一個(gè)Visit方法,這個(gè)方法對(duì)訪問(wèn)到的對(duì)象結(jié)構(gòu)中不同類型的元素做出不同的操作;在對(duì)象結(jié)構(gòu)的一次訪問(wèn)過(guò)程中,我們遍歷整個(gè)對(duì)象結(jié)構(gòu),對(duì)每一個(gè)元素都實(shí)施Accept方法,在每一個(gè)元素的Accept方法中回調(diào)訪問(wèn)者的Visit方法,從而使訪問(wèn)者得以處理對(duì)象結(jié)構(gòu)的每一個(gè)元素。我們就可以針對(duì)對(duì)象結(jié)構(gòu)設(shè)計(jì)不同的訪問(wèn)者類來(lái)完成不同的操作。
設(shè)計(jì)模式中經(jīng)常說(shuō)的一句話是:發(fā)現(xiàn)變化并封裝之。是否采用訪問(wèn)者模式,就要看“變化”是什么。訪問(wèn)者模式中,“變化”是具體訪問(wèn)者,其次是對(duì)象結(jié)構(gòu);但是,如果具體元素也會(huì)發(fā)生改變,就萬(wàn)萬(wàn)不能使用訪問(wèn)者模式,因?yàn)檫@樣“牽一發(fā)而動(dòng)全身”,后期的維護(hù)性就太差了。
上一篇:C++設(shè)計(jì)模式之狀態(tài)模式
欄 目:C語(yǔ)言
下一篇:C++中復(fù)制構(gòu)造函數(shù)和重載賦值操作符總結(jié)
本文標(biāo)題:C++設(shè)計(jì)模式之訪問(wèn)者模式
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/3287.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解
- 01-10深入C/C++浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載