詳解C++設(shè)計(jì)模式編程中對(duì)訪問(wèn)者模式的運(yùn)用
訪問(wèn)者模式(visitor),表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)穩(wěn)定的系統(tǒng)。它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開(kāi),使得操作集合可以相對(duì)自由地演化。訪問(wèn)者模式的目的是要把處理從數(shù)據(jù)結(jié)構(gòu)分離出來(lái)。很多系統(tǒng)可以按照算法和數(shù)據(jù)結(jié)構(gòu)分開(kāi),如果這樣的系統(tǒng)有比較穩(wěn)定的數(shù)據(jù)結(jié)構(gòu),又有易于變化的算法的話,使用訪問(wèn)者模式就是比較合適的,因?yàn)樵L問(wèn)者模式使得算法操作的增加變得容易。反之,如果這樣的系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)對(duì)象易于變化,經(jīng)常要有新的數(shù)據(jù)對(duì)象增加進(jìn)來(lái),就不適合使用訪問(wèn)者模式。
訪問(wèn)者模式的優(yōu)點(diǎn)就是增加新的操作很容易,因?yàn)樵黾有碌牟僮骶鸵馕吨黾右粋€(gè)新的訪問(wèn)者。訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中。通常concreteVisitor可以單獨(dú)開(kāi)發(fā),不必跟concreteElement寫在一起。訪問(wèn)者的缺點(diǎn)其實(shí)也就是使增加新的數(shù)據(jù)結(jié)構(gòu)變得困難了。
結(jié)構(gòu)圖:

訪問(wèn)者模式基本示例代碼
訪問(wèn)者模式 visitor.h、concreteVisitor.h、element.h、concreteElement.h、objectStructure.h
客戶端 visitorApp.cpp
訪問(wèn)者模式
visitor.h
/************************************************************************
* description: 為該對(duì)象結(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: 具體訪問(wèn)者,實(shí)現(xiàn)每個(gè)由visitor聲明的操作。每個(gè)操作實(shí)現(xiàn)算法
的一部分,而該算法片斷乃是對(duì)應(yīng)于結(jié)構(gòu)中對(duì)象的類
* 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訪問(wèn)" << endl;
}
virtual void visitConcreteElementB(concreteElementB* pConcreteElementB)
{
cout << "concreteElementB被concreteVisitor1訪問(wèn)" << endl;
}
};
class concreteVisitor2 : public visitor
{
public:
concreteVisitor2(){};
~concreteVisitor2(){};
virtual void visitConcreteElementA(concreteElementA* pConcreteElementA)
{
cout << "concreteElementA被concreteVisitor2訪問(wèn)" << endl;
}
virtual void visitConcreteElementB(concreteElementB* pConcreteElementB)
{
cout << "concreteElementB被concreteVisitor2訪問(wèn)" << endl;
}
};
#endif// _CONCRETE_VISITOR_H_
element.h
/************************************************************************
* description: 定義一個(gè)accept操作,它以一個(gè)訪問(wèn)者為參數(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è)高層的接口以允許訪問(wèn)者訪問(wèn)它的元素
* 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 : 定義控制臺(tái)應(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;
}
使用訪問(wèn)者模式的優(yōu)點(diǎn)和缺點(diǎn)
訪問(wèn)者模式有如下的優(yōu)點(diǎn):
- 訪問(wèn)者模式使得增加新的操作變得很容易。如果一些操作依賴于一個(gè)復(fù)雜的結(jié)構(gòu)對(duì)象的話,那么一般而言,增加新的操作會(huì)很復(fù)雜。而使用訪問(wèn)者模式,增加新的操作就意味著增加一個(gè)新的訪問(wèn)者類,因此,變得很容易。
- 訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中,而不是分散到一個(gè)個(gè)的節(jié)點(diǎn)類中。
- 訪問(wèn)者模式可以跨過(guò)幾個(gè)類的等級(jí)結(jié)構(gòu)訪問(wèn)屬于不同的等級(jí)結(jié)構(gòu)的成員類。迭代子只能訪問(wèn)屬于同一個(gè)類型等級(jí)結(jié)構(gòu)的成員對(duì)象,而不能訪問(wèn)屬于不同等級(jí)結(jié)構(gòu)的對(duì)象。訪問(wèn)者模式可以做到這一點(diǎn)。
- 積累狀態(tài)。每一個(gè)單獨(dú)的訪問(wèn)者對(duì)象都集中了相關(guān)的行為,從而也就可以在訪問(wèn)的過(guò)程中將執(zhí)行操作的狀態(tài)積累在自己內(nèi)部,而不是分散到很多的節(jié)點(diǎn)對(duì)象中。這是有益于系統(tǒng)維護(hù)的優(yōu)點(diǎn)。
訪問(wèn)者模式有如下的缺點(diǎn):
- 增加新的節(jié)點(diǎn)類變得很困難。每增加一個(gè)新的節(jié)點(diǎn)都意味著要在抽象訪問(wèn)者角色中增加一個(gè)新的抽象操作,并在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作。
- 破壞封裝。訪問(wèn)者模式要求訪問(wèn)者對(duì)象訪問(wèn)并調(diào)用每一個(gè)節(jié)點(diǎn)對(duì)象的操作,這隱含了一個(gè)對(duì)所有節(jié)點(diǎn)對(duì)象的要求:它們必須暴露一些自己的操作和內(nèi)部狀態(tài)。不然,訪問(wèn)者的訪問(wèn)就變得沒(méi)有意義。由于訪問(wèn)者對(duì)象自己會(huì)積累訪問(wèn)操作所需的狀態(tài),從而使這些狀態(tài)不再存儲(chǔ)在節(jié)點(diǎn)對(duì)象中,這也是破壞封裝的。
訪問(wèn)者模式的適用場(chǎng)景:
- 一個(gè)對(duì)象結(jié)構(gòu)包含很多類對(duì)象,它們有不同的接口,而你想對(duì)這些對(duì)象實(shí)施一些依賴于其具體類的操作。
- 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而你想避免讓這些操作“污染”這些對(duì)象的類。Vi s i t o r 使得你可以將相關(guān)的操作集中起來(lái)定義在一個(gè)類中。當(dāng)該對(duì)象結(jié)構(gòu)被很多應(yīng)用共享時(shí),用Vi s i t o r 模式讓每個(gè)應(yīng)用僅包含需要用到的操作。
- 定義對(duì)象結(jié)構(gòu)的類很少改變,但經(jīng)常需要在此結(jié)構(gòu)上定義新的操作。改變對(duì)象結(jié)構(gòu)類需要重定義對(duì)所有訪問(wèn)者的接口,這可能需要很大的代價(jià)。如果對(duì)象結(jié)構(gòu)類經(jīng)常改變,那么可能還是在這些類中定義這些操作較好。
相關(guān)文章
基于WTL 雙緩沖(double buffer)繪圖的分析詳解
本篇文章是對(duì)WTL下使用雙緩沖(double buffer)繪圖進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C++實(shí)現(xiàn)統(tǒng)計(jì)代碼運(yùn)行時(shí)間的示例詳解
這篇文章主要為大家詳細(xì)介紹了C++一個(gè)有趣的小項(xiàng)目——統(tǒng)計(jì)代碼運(yùn)行時(shí)間,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05
對(duì)C++ string append方法的常用用法詳解
今天小編就為大家分享一篇對(duì)C++ string append方法的常用用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
C語(yǔ)言實(shí)現(xiàn)病例管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)病例管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Qt實(shí)現(xiàn)小功能之復(fù)雜抽屜效果詳解
在Qt自帶的控件中,也存在抽屜控件:QToolBar。但是,該控件有個(gè)缺點(diǎn):一次只能展開(kāi)一個(gè)抽屜信息,無(wú)法實(shí)現(xiàn)多個(gè)展開(kāi)。所以本文將自定義實(shí)現(xiàn)復(fù)雜抽屜效果,需要的可以參考一下2022-10-10
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)二叉樹簡(jiǎn)單應(yīng)用
這篇文章主要介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)二叉樹簡(jiǎn)單應(yīng)用的相關(guān)資料,需要的朋友可以參考下2017-05-05
OpenSSL使用AES實(shí)現(xiàn)文件加解密功能
AES是一種對(duì)稱加密算法,它是目前廣泛使用的加密算法之一,意味著加密和解密使用相同的密鑰,這就要求密鑰的安全性非常重要,因?yàn)槿魏螕碛忻荑€的人都能進(jìn)行加密和解密操作,本文給大家介紹了OpenSSL如何使用AES實(shí)現(xiàn)文件加解密功能,需要的朋友可以參考下2023-11-11
C++ clock()解析如何使用時(shí)鐘計(jì)時(shí)的應(yīng)用
本篇文章是對(duì)c++中的clock()函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
C++用指針變量作為函數(shù)的參數(shù)接受數(shù)組的值的問(wèn)題詳細(xì)總結(jié)
以下是對(duì)C++中用指針變量作為函數(shù)的參數(shù)接受數(shù)組的值的問(wèn)題進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-10-10

