詳解C++設(shè)計(jì)模式編程中對訪問者模式的運(yùn)用
訪問者模式(visitor),表示一個(gè)作用于某對象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對穩(wěn)定的系統(tǒng)。它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開,使得操作集合可以相對自由地演化。訪問者模式的目的是要把處理從數(shù)據(jù)結(jié)構(gòu)分離出來。很多系統(tǒng)可以按照算法和數(shù)據(jù)結(jié)構(gòu)分開,如果這樣的系統(tǒng)有比較穩(wěn)定的數(shù)據(jù)結(jié)構(gòu),又有易于變化的算法的話,使用訪問者模式就是比較合適的,因?yàn)樵L問者模式使得算法操作的增加變得容易。反之,如果這樣的系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)對象易于變化,經(jīng)常要有新的數(shù)據(jù)對象增加進(jìn)來,就不適合使用訪問者模式。
訪問者模式的優(yōu)點(diǎn)就是增加新的操作很容易,因?yàn)樵黾有碌牟僮骶鸵馕吨黾右粋€(gè)新的訪問者。訪問者模式將有關(guān)的行為集中到一個(gè)訪問者對象中。通常concreteVisitor可以單獨(dú)開發(fā),不必跟concreteElement寫在一起。訪問者的缺點(diǎn)其實(shí)也就是使增加新的數(shù)據(jù)結(jié)構(gòu)變得困難了。
結(jié)構(gòu)圖:
訪問者模式基本示例代碼
訪問者模式 visitor.h、concreteVisitor.h、element.h、concreteElement.h、objectStructure.h
客戶端 visitorApp.cpp
訪問者模式
visitor.h /************************************************************************ * description: 為該對象結(jié)構(gòu)中ConcreteElement的每一個(gè)類聲明一個(gè)visit操作 * remark: ************************************************************************/ #ifndef _VISITOR_H_ #define _VISITOR_H_ class concreteElementA; class concreteElementB; class visitor { public: visitor(){}; virtual ~visitor(){}; virtual void visitConcreteElementA(concreteElementA* pConcreteElementA) = 0; virtual void visitConcreteElementB(concreteElementB* pConcreteElementB) = 0; }; #endif// _VISITOR_H_
concreteVisitor.h
/************************************************************************ * description: 具體訪問者,實(shí)現(xiàn)每個(gè)由visitor聲明的操作。每個(gè)操作實(shí)現(xiàn)算法 的一部分,而該算法片斷乃是對應(yīng)于結(jié)構(gòu)中對象的類 * remark: ************************************************************************/ #ifndef _CONCRETE_VISITOR_H_ #define _CONCRETE_VISITOR_H_ #include "visitor.h" #include <iostream> using namespace std; class concreteVisitor1 : public visitor { public: concreteVisitor1(){}; ~concreteVisitor1(){}; virtual void visitConcreteElementA(concreteElementA* pConcreteElementA) { cout << "concreteElementA被concreteVisitor1訪問" << endl; } virtual void visitConcreteElementB(concreteElementB* pConcreteElementB) { cout << "concreteElementB被concreteVisitor1訪問" << endl; } }; class concreteVisitor2 : public visitor { public: concreteVisitor2(){}; ~concreteVisitor2(){}; virtual void visitConcreteElementA(concreteElementA* pConcreteElementA) { cout << "concreteElementA被concreteVisitor2訪問" << endl; } virtual void visitConcreteElementB(concreteElementB* pConcreteElementB) { cout << "concreteElementB被concreteVisitor2訪問" << endl; } }; #endif// _CONCRETE_VISITOR_H_
element.h
/************************************************************************ * description: 定義一個(gè)accept操作,它以一個(gè)訪問者為參數(shù) * remark: ************************************************************************/ #ifndef _ELEMENT_H_ #define _ELEMENT_H_ class visitor; class element { public: element(){}; virtual ~element(){}; virtual void accept(visitor* pVisitor) = 0; }; #endif// _ELEMENT_H_
concreteElement.h
#ifndef _CONCRETE_ELEMENT_H_ #define _CONCRETE_ELEMENT_H_ #include "element.h" #include <iostream> using namespace std; class concreteElementA : public element { public: concreteElementA(){}; ~concreteElementA(){}; // 充分利用雙分派技術(shù),實(shí)現(xiàn)處理與數(shù)據(jù)結(jié)構(gòu)的分離 virtual void accept(visitor* pVisitor) { if (NULL != pVisitor) { pVisitor->visitConcreteElementA(this); } } // 其他的相關(guān)方法 void operationA() { cout << "具體元素A的其他相關(guān)方法" << endl; } }; class concreteElementB : public element { public: concreteElementB(){}; ~concreteElementB(){}; // 充分利用雙分派技術(shù),實(shí)現(xiàn)處理與數(shù)據(jù)結(jié)構(gòu)的分離 virtual void accept(visitor* pVisitor) { if (NULL != pVisitor) { pVisitor->visitConcreteElementB(this); } } // 其他的相關(guān)方法 void operationB() { cout << "具體元素B的其他相關(guān)方法" << endl; } }; #endif// _CONCRETE_ELEMENT_H_
objectStructure.h
/************************************************************************ * description: 枚舉元素,可以提供一個(gè)高層的接口以允許訪問者訪問它的元素 * remark: ************************************************************************/ #ifndef _OBJECT_STRUCTURE_H_ #define _OBJECT_STRUCTURE_H_ #include "element.h" #include "visitor.h" #include <list> using namespace std; class objectStructure { public: void attach(element* pElement) { m_list.push_back(pElement); } void detach(element* pElement) { m_list.remove(pElement); } void accept(visitor* pVisitor) { list<element*>::iterator Iter; for (Iter = m_list.begin(); Iter != m_list.end(); ++Iter) { if (NULL != *Iter) { (*Iter)->accept(pVisitor); } } } private: list<element*> m_list; }; #endif// _OBJECT_STRUCTURE_H_
客戶端
visitorApp.cpp
// visitorApp.cpp : 定義控制臺應(yīng)用程序的入口點(diǎn)。 // #include "stdafx.h" #include "objectStructure.h" #include "concreteElement.h" #include "concreteVisitor.h" void freePtr(void* vptr) { if (NULL != vptr) { delete vptr; vptr = NULL; } } int _tmain(int argc, _TCHAR* argv[]) { objectStructure* pObject = new objectStructure(); if (NULL != pObject) { element* pElementA = new concreteElementA(); element* pElementB = new concreteElementB(); pObject->attach(pElementA); pObject->attach(pElementB); concreteVisitor1* pVisitor1 = NULL; pVisitor1 = new concreteVisitor1(); concreteVisitor2* pVisitor2 = NULL; pVisitor2 = new concreteVisitor2(); pObject->accept(pVisitor1); pObject->accept(pVisitor2); system("pause"); freePtr(pVisitor2); freePtr(pVisitor1); freePtr(pElementB); freePtr(pElementA); freePtr(pObject); } return 0; }
使用訪問者模式的優(yōu)點(diǎn)和缺點(diǎn)
訪問者模式有如下的優(yōu)點(diǎn):
- 訪問者模式使得增加新的操作變得很容易。如果一些操作依賴于一個(gè)復(fù)雜的結(jié)構(gòu)對象的話,那么一般而言,增加新的操作會很復(fù)雜。而使用訪問者模式,增加新的操作就意味著增加一個(gè)新的訪問者類,因此,變得很容易。
- 訪問者模式將有關(guān)的行為集中到一個(gè)訪問者對象中,而不是分散到一個(gè)個(gè)的節(jié)點(diǎn)類中。
- 訪問者模式可以跨過幾個(gè)類的等級結(jié)構(gòu)訪問屬于不同的等級結(jié)構(gòu)的成員類。迭代子只能訪問屬于同一個(gè)類型等級結(jié)構(gòu)的成員對象,而不能訪問屬于不同等級結(jié)構(gòu)的對象。訪問者模式可以做到這一點(diǎn)。
- 積累狀態(tài)。每一個(gè)單獨(dú)的訪問者對象都集中了相關(guān)的行為,從而也就可以在訪問的過程中將執(zhí)行操作的狀態(tài)積累在自己內(nèi)部,而不是分散到很多的節(jié)點(diǎn)對象中。這是有益于系統(tǒng)維護(hù)的優(yōu)點(diǎn)。
訪問者模式有如下的缺點(diǎn):
- 增加新的節(jié)點(diǎn)類變得很困難。每增加一個(gè)新的節(jié)點(diǎn)都意味著要在抽象訪問者角色中增加一個(gè)新的抽象操作,并在每一個(gè)具體訪問者類中增加相應(yīng)的具體操作。
- 破壞封裝。訪問者模式要求訪問者對象訪問并調(diào)用每一個(gè)節(jié)點(diǎn)對象的操作,這隱含了一個(gè)對所有節(jié)點(diǎn)對象的要求:它們必須暴露一些自己的操作和內(nèi)部狀態(tài)。不然,訪問者的訪問就變得沒有意義。由于訪問者對象自己會積累訪問操作所需的狀態(tài),從而使這些狀態(tài)不再存儲在節(jié)點(diǎn)對象中,這也是破壞封裝的。
訪問者模式的適用場景:
- 一個(gè)對象結(jié)構(gòu)包含很多類對象,它們有不同的接口,而你想對這些對象實(shí)施一些依賴于其具體類的操作。
- 需要對一個(gè)對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作“污染”這些對象的類。Vi s i t o r 使得你可以將相關(guān)的操作集中起來定義在一個(gè)類中。當(dāng)該對象結(jié)構(gòu)被很多應(yīng)用共享時(shí),用Vi s i t o r 模式讓每個(gè)應(yīng)用僅包含需要用到的操作。
- 定義對象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。改變對象結(jié)構(gòu)類需要重定義對所有訪問者的接口,這可能需要很大的代價(jià)。如果對象結(jié)構(gòu)類經(jīng)常改變,那么可能還是在這些類中定義這些操作較好。
相關(guān)文章
基于C語言實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于C語言實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Qt中QList與QLinkedList類的常用方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了Qt中QList與QLinkedList類的常用方法,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Qt有一定的幫助,需要的可以參考一下2022-12-12C語言字符串與字符數(shù)組面試題中最易錯(cuò)考點(diǎn)詳解
這篇文章主要介紹了C語言字符串與字符數(shù)組面試題中最易錯(cuò)考點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09C++求所有頂點(diǎn)之間的最短路徑(用Floyd算法)
這篇文章主要為大家詳細(xì)介紹了C++求所有頂點(diǎn)之間的最短路徑,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04基于Matlab制作一個(gè)不良圖片檢測系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何基于Matlab制作一個(gè)不良圖片檢測系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的可以跟隨小編一起了解一下2022-07-07詳解數(shù)據(jù)結(jié)構(gòu)C語言實(shí)現(xiàn)之循環(huán)隊(duì)列
在我們生活中有很多隊(duì)列的影子,可以說與時(shí)間相關(guān)的問題,一般都會涉及到隊(duì)列問題;本文詳細(xì)介紹了如何使用C語言實(shí)現(xiàn)循環(huán)隊(duì)列,下面一起來看看。2016-07-07