1. 程式人生 > >【設計模式】趣說訪問者模式,頗有些無奈之舉

【設計模式】趣說訪問者模式,頗有些無奈之舉


老實說,在實際程式設計中,訪問者設計模式應用的並不多,至少我是這樣認為的,因為它的主要使用場景並不多。
那麼肯定會有人問,訪問者模式的主要使用場景是什麼呢?繼續往下看便知。


新聞聯播看多了之後


首先要說的是,設計模式中的“訪問者”和現實生活中的“訪問者”其本質是一回事。雖然設計模式中的不太熟悉,但現實生活中的再熟悉不過了。

我在以前的文章中多次提到過,有時站在現實生活的角度看待某些技術點反而會更容易看清楚,那照例還是從生活中的事情說起吧。

說起訪問者,我能夠想到最高大上的,莫過於國家領導人的國事訪問。以中方訪問美方來說吧,這裡面的大致內容(我猜)應該是這樣的:


中方專機什麼時間在哪個機場降落,美方派誰在機場迎接,然後就是歡迎儀式和歡迎晚宴,接著就是會見哪些人,開哪些會議,簽署哪些檔案,參觀哪些地方等等,最後就是結束訪問啟程回國。

當然這些內容肯定是雙方外交部門都提前溝通好的。但是仍然是在美方的安排下一步一步進行,因為我們是作為訪客的身份,人家是東道主,要盡地主之誼的。(雖然老美不是什麼好東西)

比如人家安排的吃牛排,那我們就吃吧。我們總不能要求他們改成“主席套餐”吧,再說他們的廚師也搞定啊。

等到老美的總統來我們國家訪問的時候,他們就成了訪客了,我們就是東道主,整體就得由我們來安排。讓他喝稀飯他就不能吃大米,不喜歡吃就晚上自己回酒店泡方便麵,哈哈哈哈。

如果把這個事情抽象一下就是,一方在另一方的安排下,逐步有序的做一些事情。


自己想體驗的話就來個這吧


國事訪問這事啊離我們太遙遠了,那就再看個和我們息息相關的吧。沒錯,就是旅遊。無論是國內遊還是出境遊,其實差別不大,大致內容應該是這樣的:

先報好旅行社,在指定的時間乘坐交通工具到達目的地後,會有一輛大巴車拉著我們,按照行程開始去景點,去吃飯,去酒店等等。

我們什麼都不用操心,跟著走就行了,因為旅行社和導遊都安排好了。再說了,即我們使有意見,導遊也不會聽我們的。

頗令人討厭的可能就是逛購物店了,但是沒辦法,因為協議已經簽了。我們有義務進購物店,聽相關人員講解,想買的就買,不想買的隨便看看,但是不能提前出去。

其實還有更坑的,那就是導遊在大巴車上強行收費,說些很難聽的話,甚至罵人/威脅。尤其是在境外,他們覺得此生很難再見面,有時話說的特別難聽。

所以整個行程下來,既有高興的時候,也有心煩的時候。導遊給我們講解當地歷史的時候,覺得他是“好人”,領我們進購物店時,又覺得他是“壞人”。

其實都不是,他是“工人”,一個從事旅遊行業工作的人。一個需要養家餬口的人,跟我們沒啥區別。我是不是很善解人意啊。

如果把這個事情也抽象一下就是,必須按照既定的規則走完所有事項,如果對某個事項關心,那就積極的去獲取自己想要的資訊,如果對某個事項不關心,那就默默的跟隨即可,什麼都不用做,但是不準離開。


做一個善於思考總結的人


我想說的是既然報團遊有如此多的問題,為什麼還有那麼多人報團,而不選自由行呢。答案是顯而易見的。

以出境游來說,當你達到國外,人生地不熟,語言又不通,很多事情的推進會特別艱難。

當然也可以提前研究攻略,制定好路線,訂好酒店機票等。但是旅遊主要是想放鬆一下,為了出國一週,在家看三個月攻略,豈不是更累嗎?

總結一下,當你對所要做的事情完全不瞭解,而且想要了解的話需花費很大的精力時,只能選擇第三方給出的方案,雖然明知裡面可能會有坑。

對於像國事訪問的,因為有許多禮儀禮節或規則約束需要遵守,所以一般也都聽從東道主的安排。

雖然出訪和旅遊這兩件事的本質完全不一樣,但是他們的巨集觀進行模式卻基本一致。

到此我們已經講了兩件事,兩個特點,兩個原因。請仔細體會下。看起來有點讓人不爽,但又頗有些無奈。

這兩件事都是站在“訪問者”的立場來說的,下面從多角度來看下。


從一個具體的示例說起


假如小明在北京工作多年,對北京非常熟悉。他的朋友小白來找他玩,而且是第一次來北京,打算去一些有名的景點。

在這件事中,小明就是東道主,小白就是訪客。其實就是一方帶另一方參觀嘛。

站在東道主的角度,他要安排訪客參觀景點,所以是這樣的:

/**
 * <p>東道主
 */
public interface Host {

    //帶朋友去故宮
    void show(PalaceMuseum PalaceMuseum, Guest guest);

    //帶朋友去長城
    void show(GreatWall GreatWall, Guest guest);

    //帶朋友去頤和園
    void show(SummerPalace SummerPalace, Guest guest);
}

 


站在訪客的角度,他是要參觀景點的,所以是這樣的:

/**
 * <p>客人
 */
public interface Guest {

    //看故宮
    void look(PalaceMuseum PalaceMuseum);

    //看長城
    void look(GreatWall GreatWall);

    //看頤和園
    void look(SummerPalace SummerPalace);
}



站在景點的角度,它是要接受訪客的參觀的,所以是這樣的:

/**
 * <p>故宮
 */
public interface PalaceMuseum {

    //讓訪客看
    void accept(Guest guest);
}

/**
 * <p>長城
 */
public interface GreatWall {

    //讓訪客看
    void accept(Guest guest);
}

/**
 * <p>頤和園
 */
public interface SummerPalace {

    //讓訪客看
    void accept(Guest guest);
}

 

 

在這個事件中共有三種角色,它們的職責、目的和作用都非常清晰:

小明:東道主,職責是負責協助

小白:訪客,目的是欣賞景點的景色

景點:被欣賞者,作用是提供景色

這就是一個訪問者的模型,我們把它抽象並一般化,發現這是一個固定的套路或模式,稱之為訪問者模式。

在訪問者模式中,共有三方參與者,它們的分工非常明確:

一方:訪問者,獲取資訊的人

二方:被訪問者,提供資訊的人

三方:協調者,安排一二雙方進行互動的人

可以這樣來理解三方的定位,一方是購買者(出錢),二方是提供者(出力),三方是協調者(和稀泥)。哈哈。

注意,這裡的一方二方三方都是訪問者模式內部的概念,它們是一家人或一個單位的。

換個角度來看就是,訪問者在協調者制定的規則下完成對被訪問者的訪問,期間獲取關心的資訊,忽略不關心的資訊。

把訪問者模式放到一個巨集觀應用中,應該是這樣的:


使用者程式->|訪問者->協調者->被訪問者|->底層複雜資料



訪問者模式的推導


對於設計模式,一定要活學活用,不能拘泥於GOF。一定要按自己的場景需求來用,不能死搬硬套。

在訪問者模式中,通常把被訪問者稱為元素,訪問者自然還是訪問者,抽象一下:

//元素
public interface Element<V extends Visitor> {

    //接受訪問者
    void accept(V visitor);
}

//訪問者
public interface Visitor<E extends Element> {

    //訪問元素
    void visit(E element);
}


請注意這裡的泛型。

然後再抽象一下協調者:

 

public interface ObjectStructure {

    //所有元素
    List<Element> getElements();

    //所有訪問者
    List<Visitor> getVisitors();
}



協調者擁有所有的元素和所有的訪問者,它可以自己來實現訪問規則,使訪問者完成對元素的訪問。

注意,我的抽象和GOF的不完全一樣,因為前面已經說了要活學活用嘛。

那究竟是一個訪問者訪問一個元素,還是一個訪問者訪問多個元素,仍然是沒有標準答案,應該根據實際情況來定。

比如大公司,一崗(位)一(個)人,事情做得精細。小公司,多崗(位)一(個)人,辦事效率高。各有千秋,適合的才是最好的。

下面給出一個訪問者訪問一個元素的情況。

訪問者A訪問元素A:

//元素A
public interface ElementA extends Element<VisitorA> {

    @Override
    void accept(VisitorA visitor);
}

//訪問者A
public interface VisitorA extends Visitor<ElementA> {

    @Override
    void visit(ElementA element);
}


由於上面使用了泛型,這裡的方法引數可以換為精確的型別。

訪問者B訪問元素B:

//元素B
public interface ElementB extends Element<VisitorB> {

    @Override
    void accept(VisitorB visitor);
}

//訪問者B
public interface VisitorB extends Visitor<ElementB> {

    @Override
    void visit(ElementB element);
}


後續就按照這個模式去擴充套件即可。有新的資料需要訪問時,就新增新的元素和新的訪問者,同時還可能需要修改協調者。


作者想要說的話


我們經常聽到說,要學習西方模式,XX模式,YY模式等,其實主要是學習人家的理念和思想,而不是照抄,因為照抄有水土不服的問題。

所以,每一個開發人員或設計人員都不應該直接照抄GOF的設計模式。特別是為了使用而使用,就更沒意思了。

訪問者模式的主要應用場景之一就是,底層資料過於複雜,是的,過於複雜,上層應用無法直接訪問。

如Java的位元組碼檔案,我們的應用程式根本就無法直接訪問。

還有一種就是不想讓別人隨意訪問,可以通過訪問者模式去約束訪問者訪問的方式。

比如我在大四時就去參觀過汽車製造車間,由專人領著我們按照路線行走,因為隨意亂跑不安全嘛。

訪問者模式的中心思想就是,協調者制定好合理的規則,訪問者按照規則進行訪問,從自己關心的被訪者上獲取需要的資料資訊。

當然也可以採用被動式的,協調者按照一定規則,把被訪問者的資訊逐步推送給訪問者,訪問者根據自己的需要來選擇儲存或忽略。

最後,設計模式是一種理念,一種思想,需要去思考,去領悟。

程式程式碼:
https://github.com/coding-new-talking/java-code-demo.git

 

 

 

(END)

 

作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號的二維碼,歡迎關注!

 &nb