設計模式之訪問者模式

訪問者模式

  訪問者(Visitor)模式,表示一個做用於某對象結構中各個元素的操做。它使你能夠在不改變各元素的前提下定義做用於這些元素的新操做。linux

訪問者模式UML類圖

Visitor:爲該對象結構中ConcreteElement的每個具體類聲明一個Visit操做。它的參數就是能夠訪問的元素,它的方法個數理論上來講是和ConcreteElement具體子類的個數是一致的。所以訪問者模式要求元素(Element)的類組要穩定,若是要常常添加、移除元素類,那麼必然會致使頻繁的修改Visitor接口,這不適合訪問者模式。ios

ConcreteVisitor:具體的訪問者,實現由每一個Visitor聲明的操做。每一個操做實現算法的一部分,而該算法片斷乃是對應於結構中對象的類。(就是要給出對每個ConcreteElement類進行訪問時所要產生的具體行爲)。算法

Element:定義一個Accept操做,它以一個訪問爲參數。意思是每個元素都要能夠被訪問者訪問。設計模式

ConcreteElement:具體元素類,實現Accept操做。它實現接受訪問操做的具體實現,而這個具體的實現,一般狀況下是使用訪問者提供的訪問該元素類的方法。數據結構

ObjectStructure:能枚舉它的元素,能夠提供一個高層接口以容許訪問者訪問它的元素,(這個就是模式定義中和Visitor類解釋中提到的對象結構)。對象結構是一個抽象表述,具體點能夠理解爲一個具備容器性質或者複合對象特性的類,它會含有一組元素(Element),並能夠迭代,以便訪問者訪問。ide

  在上面的5個角色中,最重要的就是ObjectStructure,所謂訪問者模式就是爲了訪問者能夠方便的訪問對象結構而存在的。所謂的狀態,其實就是訪問者根據對象可能有的某個狀態作出不一樣的反應來實現的。ui

訪問者模式的優缺點

優勢:this

  1.增長新的訪問操做很方便,使用訪問者模式,增長新的訪問操做,實際上就是增長一個新的具體訪問類,實現不須要修改已有的代碼,符合開閉原則。spa

  2.將有關元素對象的訪問行爲集中在了一個訪問者類中,而不是分散在各個不一樣的元素類裏,類的職責更加清晰,有利於對象結構中元素對象的複用。相同的對象結構,能夠供多個不一樣的訪問者訪問。設計

  3.讓用戶可以在不修改現有元素類層次結構的狀況,定義做用於該層次結構的操做。

缺點:

  1.增長新的元素類困難,每新增一個元素類,都意味着要修改Visitor類,而且須要在每個具體訪問者類中增長具體的訪問實現,違反了開放-封閉原則。(若是對象結構不穩定的話,說明是不適用訪問者模式的)

  2.破壞了封裝

適用場景:

  1.訪問者模式適用於數據結構相對穩定的系統。它把數據結構和做用於結構之上的操做之間的耦合解脫開,使得操做集合能夠相對自由的演化。使用訪問者模式的目的是要把處理從數據結構中分離出來。不少系統能夠按照算法和數據結構分開,若是這樣的系統有比較穩定的數據結構,又有易於變化的算法的話,使用訪問者模式就是比較合適的,由於訪問者模式使得算法的操做變的更加容易。反之,若是這樣的系統的數據結構對象易於變化,常常要有新的數據對象增長進來,那麼是不適合使用訪問者模式的。

  2.ConcreteVisitor一般能夠單獨開發,沒必要跟ConcreteElement寫在一塊兒。這樣就提升了ConcreteElement的獨立性,若是把訪問操做設計爲ConcreteElement的方法,那麼每次新增操做都要修改ConcreteElement的源碼,這就形成了數據結構和處理方法之間的緊耦合。

代碼示例

  案例來源於《大話設計模式》的男人和女人的例子,本文中使用C++在linux下實現。 在這裏例子中Element就是人,人只分爲男人和女人,因此ConcreteElement就只有男人類和女人類兩種,所以對於人這個元素來講,它的對象結構是穩定的。人的一輩子有不少狀態,例如成功/失敗,結婚/不結婚,戀愛/結婚等等。對每一種狀態,男人和女人的反應是不同的,訪問者類Visitor的做用就是根據不一樣的狀態獲得男人和女人的不一樣反應,若是有新的狀態,那麼就增長一個新的訪問者類便可。男人和女人類都須要提供一個accept方法,參數是一個訪問者,一般是使用訪問者提供的訪問該元素類的方法,獲得男人和女人在面對某一狀態下的反應。對象結構,須要維護一個Element的列表,這個列表是能夠迭代的,以方便訪問者訪問不一樣Element實例。

1.Element和Visitor類

#ifndef VISITOR_H_
#define VISITOR_H_
//Action類就是訪問者模式中的Visitor類。之因此起名就作Action,是由於它根據不一樣的狀態對元素採用了不一樣的操做
//具體元素類只分兩種,男人類和女人類,訪問者須要提供針對這種類型的訪問操做
//因此Action類根本上是一個狀態類
class Man;
class Woman;
#include <iostream>
#include <typeinfo>

class Action
{
public:
    virtual void visitMan(Man ConcreteElementA) = 0;    //訪問男人類(實際上就是根據狀態的不一樣獲得男人的反應)
    virtual void visitWoman(Woman ConcreteElementB) = 0;    //訪問女人類(根據狀態的不一樣獲得女人的反應)
    Action() = default;
    virtual ~Action() = default;
};

//Human就至關因而訪問者模式中的Element,
class Human
{
public:
    virtual void accept(Action *visitor) = 0;
    Human() = default;
    virtual ~Human() = default;
};
#endif
Visitor&Elment

2.ConcreteElement類

#ifndef MAN_H_
#define MAN_H_

#include "VisitorPattern.h"

class Man:public Human
{
public:
    void accept(Action *visitor) override;
    Man() = default;
    ~Man() = default;
};
#endif

#include "Man.h"

void Man::accept(Action *visitor)
{
    visitor->visitMan(*this);
}

#ifndef WOMAN_H_
#define WOMAN_H_

#include "VisitorPattern.h"

class Woman: public Human
{
public:
    void accept(Action *visitor) override;
    Woman() = default;
    ~Woman() = default;
};
#endif

#include "Woman.h"

void Woman::accept(Action *visitor)
{
    visitor->visitWoman(*this);
}
ConcreteElement

3.ConcreteVisitor類

#ifndef SUCCESS_H_
#define SUCCESS_H_

#include "Man.h"
#include "Woman.h"

class Success : public Action
{
public:
    void visitMan(Man concreteElementA) override;
    void visitWoman(Woman concreteElementB) override;
    Success() = default;
    ~Success() = default;
};
#endif

#include "Success.h"

void Success::visitMan(Man concreteElementA)
{
    std::cout << typeid(concreteElementA).name() << typeid(*this).name() << "shi,bei hou duo ban you yi ge wei da de nv ren." << std::endl;
}

void Success::visitWoman(Woman concreteElementB)
{
    std::cout << typeid(concreteElementB).name() << typeid(*this).name() << "shi,beihou da duo you yi ge bu cheng gong de nan ren." << std::endl;
}

#ifndef FAILED_H_
#define FAILED_H_

#include "VisitorPattern.h"
#include "Man.h"
#include "Woman.h"

class Failed :public Action
{
public:
    void visitMan(Man concreteElementA) override;
    void visitWoman(Woman concreteElementB) override;
    Failed() = default;
    ~Failed() = default;
};
#endif

#include "Failed.h"

void Failed::visitMan(Man concreteElementA)
{
    std::cout << typeid(concreteElementA).name() << typeid(*this).name() << "shi,men tou he jiu,shui ye bu yong quan." << std::endl;
}

void Failed::visitWoman(Woman concreteElementB)
{
    std::cout << typeid(concreteElementB).name() << typeid(*this).name() << "shi,yan lei wang wang,shui ye quan bu liao." << std::endl;
}
ConcreteVisitor

4.ObjectStructure類

#ifndef OBJECTSTRUCTURE_H_
#define OBJECTSTRUCTURE_H_

//對象結構

#include "VisitorPattern.h"
#include <list>

class ObjectStructure
{
private:
    std::list<Human *> elements;
public:
    //增長
    void add(Human *objHuman);
    //移除
    void remove(Human *objHuman);
    //查看顯示
    void display(Action *visitor);
    ObjectStructure() = default;
    ~ObjectStructure() = default;
};
#endif

#include "ObjectStructure.h"

void ObjectStructure::add(Human *objHuman)
{
   elements.push_back(objHuman);
}

void ObjectStructure::remove(Human *objHuman)
{
    elements.remove(objHuman);
}

void ObjectStructure::display(Action *visitor) 
{
    for(auto value : elements)
    {
    value->accept(visitor);
    }
}
ObjectStructure

5.Client

#include "ObjectStructure.h"
#include "Man.h"
#include "Woman.h"
#include "Success.h"
#include "Failed.h"

using namespace std;

int main(int argc,char *argv[])
{
    ObjectStructure objObjectStructure;
    Man objMan;
    Woman objWoman;
    objObjectStructure.add(&objMan);
    objObjectStructure.add(&objWoman);
    
    //成功時的反應
    Success objSuccess;
    objObjectStructure.display(&objSuccess);
    //失敗時的反應
    Failed objFailed;
    objObjectStructure.display(&objFailed);
    
    return (1);
}
Client
相關文章
相關標籤/搜索