33.3 包裝模式群PK
我們講了這么多的設計模式,大家有沒有發覺在很多的模式中有些角色是不干活的?它們只是充當黔首作用,你有問題,找我,但我不處理,我讓其他人處理。最典型的就是代理模式了,代理角色接收請求然后傳遞到被代理角色處理。門面模式也是一樣,門面角色的任務就是把請求轉發到子系統。類似這種結構的模式還有很多,我們先給這種類型的模式定義一個名字,叫做包裝模式(wrapping pattern)。注意,包裝模式是一組模式而不是一個。包裝模式包括哪些設計模式呢?包裝模式包括:裝飾模式、適配器模式、門面模式、代理模式、橋梁模式。下面我們通過一組例子來說明這五個包裝模式的區別。
33.3.1 代理模式
現在很多明星都有經紀人,一般有什么事他們都會說:“你找我的經紀人談好了”,下面我們就看看這一過程怎么模擬。假設有一個追星族想找明星簽字,我們看看采用代理模式怎么實現。代理模式是包裝模式中的最一般的實現,類圖如圖33-6所示。

圖33-6 追星族找明星簽字
類圖很簡單,就是一個簡單的代理模式,我們來看明星的定義,明星接口如代碼清單33-29所示。
代碼清單33-29 明星接口
public?interface?IStar?{
?????//明星都會簽名
?????public?void?sign();
}
明星只有一個行為:簽字。我們來看明星的實現,如代碼清單33-30所示。
代碼清單33-30 明星
public?class?Singer?implements?IStar?{
?????public?void?sign()?{
?????????????System.out.println("明星簽字:我是XXX大明星");
?????}
}
經紀人與明星應該有相同的行為,比如說簽名,雖然經紀人不簽名,但是他把你要簽名的筆記本、衣服、CD等傳遞過去讓真正的明星簽字,經紀人如代碼清單33-31所示。
代碼清單33-31 經紀人
public?class?Agent?implements?IStar?{
?????//定義是誰的經紀人
?????private?IStar?star;
?????//構造函數傳遞明星
?????public?Agent(IStar?_star){
?????????????this.star?=?_star;
?????}
?????//經紀人是不會簽字的,簽字了歌迷也不認
?????public?void?sign()?{
?????????????star.sign();
?????}
}
應該非常明確地指出一個經紀人是誰的代理,因此要在構造函數中接收一個明星對象,確定是要做這個明星的代理。我們再來看看追星族是怎么找明星簽字的,如代碼清單33-32所示。
代碼清單33-32 追星族
public?class?Idolater?{
?????public?static?void?main(String[]?args)?{
?????????????//崇拜的明星是誰
?????????????IStar?star?=?new?Singer();
?????????????//找到明星的經紀人
?????????????IStar?agent?=?new?Agent(star);
?????????????System.out.println("追星族:我是你的崇拜者,請簽名!");
?????????????//簽字
?????????????agent.sign();
?????}
}
很簡單,找到明星的代理,然后明星就簽字了。運行結果如下所示:
追星族:我是你的崇拜者,請簽名!
明星簽字:我是XXX大明星
看看我們的程序邏輯,我們是找明星的經紀人簽字,真實簽字的是明星,經紀人只是把這個請求傳遞給明星處理而已,這是普通的代理模式的典型應用。
33.3.2 裝飾模式
明星也都是一步一步地奮斗出來的,誰都不是一步就成為大明星的。甚至一些演員通過粉飾自己給觀眾一個好的印象,現在我們就來看怎么粉飾一個演員,如圖33-7所示。

圖33-7 演技修飾
下面我們就來看看這些過程如何實現,先看明星接口,如代碼清單33-33所示。
代碼清單33-33 明星接口
public?interface?IStar?{
?????//演戲
?????public?void?act();
}
我們來看看我們的主角,如代碼清單33-34所示。
代碼清單33-34 假明星
public?class?FreakStar?implements?IStar?{
?????public?void?act()?{
?????????????System.out.println("演中:演技很拙劣");
?????}
}
我們看看這個明星是怎么粉飾的,先定義一個抽象裝飾類,如代碼清單33-35所示。
代碼清單33-35 抽象裝飾類
public?abstract?class?Decorator?implements?IStar?{
?????//粉飾的是誰
?????private?IStar?star;
?????public?Decorator(IStar?_star){
?????????????this.star?=?_star;
?????}
?????public?void?act()?{
?????????????this.star.act();
?????}
}
前后兩次修飾,開演前毫無忌憚地吹噓,如代碼清單33-36所示。
代碼清單33-36 吹大話
public?class?HotAir?extends?Decorator?{
?????public?HotAir(IStar?_star){
?????????????super(_star);
?????}
?????public?void?act(){
?????????????System.out.println("演前:夸夸其談,沒有自己不能演的角色");
?????????????super.act();
?????}
}
大家發現這個明星演技不好的時候,他拼命找借口,說是那天天氣不好、心情不好等,如代碼清單33-37所示。
代碼清單33-37 抵賴
public?class?Deny?extends?Decorator?{
?????public?Deny(IStar?_star){
?????????????super(_star);
?????}
?????public?void?act(){
?????????????super.act();
?????????????System.out.println("演后:百般抵賴,死不承認");
?????}
}
我們建立一個場景把這種情況展示一下,如代碼清單33-38所示。
代碼清單33-38 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義出所謂的明星
?????????????IStar?freakStar?=?new?FreakStar();
?????????????//看看他是怎么粉飾自己的
?????????????//演前吹噓自己無所不能
?????????????freakStar?=?new?HotAir(freakStar);
?????????????//演完后,死不承認自己演的不好
?????????????freakStar?=?new?Deny(freakStar);
?????????????System.out.println("====看看一些虛假明星的形象====");
?????????????freakStar.act();
?????}
}
運行結果如下所示:
====看看一些虛假明星的形象====
演前:夸夸其談,沒有自己不能演的角色
演中:演技很拙劣
演后:百般抵賴,死不承認
33.3.3 適配器模式
我們知道在演藝圈中還存在一種情況:替身,替身也是演員,只是普通的演員而已,在一段戲中,前十五分鐘是明星本人,后十五分鐘也是明星本人,就中間的五分鐘是替身,那這個場景該怎么描述呢?注意中間那五分鐘,這個時候一個普通演員被導演認為是明星演員,我們來看類圖,如圖33-8所示。

圖33-8 替身演員類圖
導演找了一個普通演員作為明星的替身,不過觀眾看到的還是明星的身份。我們來看代碼,首先看明星接口,如代碼清單33-39所示。
代碼清單33-39 明星接口
public?interface?IStar?{
?????//明星都要演戲
?????public?void?act(String?context);
}
再來看一個具體的電影明星,他的主要職責就是演戲,如代碼清單33-40所示。
代碼清單33-40 電影明星
public?class?FilmStar?implements?IStar?{
?????public?void?act(String?context)?{
?????????????System.out.println("明星演戲:"?+?context);
?????}
}
我們再來看普通演員,明星就那么多,但是普通演員非常多,我們看其接口,如代碼清單33-41所示。
代碼清單33-41 普通演員接口
public?interface?IActor?{
?????//普通演員演戲
?????public?void?playact(String?contet);
}
普通演員也是演員,是要演戲的,我們來看一個普通演員的實現,如代碼清單33-42所示。
代碼清單33-42 普通演員
public?class?UnknownActor?implements?IActor?{
?????//普通演員演戲
?????public?void?playact(String?context)?{
?????????????System.out.println("普通演員:"+context);
?????}
}
我們來看替身該怎么編寫,如代碼清單33-43所示。
代碼清單33-43 替身演員
public?class?Standin?implements?IStar?{
?????private?IActor?actor;
?????//替身是誰
?????public?Standin(IActor?_actor){
?????????????this.actor?=?_actor;
?????}
?????public?void?act(String?context)?{
?????????????actor.playact(context);
?????}
}
這是一個通用的替身,哪個普通演員能擔任哪個明星的替身是由導演決定的,導演想讓誰當就讓誰當,我們來看導演,如代碼清單33-44所示。
代碼清單33-44 導演類
public?class?direcotr?{
?????public?static?void?main(String[]?args)?{
?????????????System.out.println("=======演戲過程模擬==========");
?????????????//定義一個大明星
?????????????IStar?star?=?new?FilmStar();
?????????????star.act("前十五分鐘,明星本人演戲");?????
?????????????//導演把一個普通演員當做明星演員來用
?????????????IActor?actor?=?new?UnknownActor();
?????????????IStar?standin=?new?Standin(actor);
?????????????standin.act("中間五分鐘,替身在演戲");
?????????????star.act("后十五分鐘,明星本人演戲");?????
?????}
}
運行結果如下所示:
=======演戲過程模擬==========
明星演戲:前十五分鐘,明星本人演戲
普通演員:中間五分鐘,替身在演戲
明星演戲:后十五分鐘,明星本人演戲
這里使用了適配器模式,把一個普通的演員轉換為一個明星演員。
33.3.4 橋梁模式
我們繼續說明星圈的事情,現在明星類型太多了,比如電影明星、電視明星、歌星、體育明星、網絡明星等,每個類型的明星都有明確的職責,電影明星的主要工作就是演電影,電視明星的主要工作就是演電視劇或者主持電視節目。再看看現在的明星,單一發展的基本沒有,主持人出專輯、體育明星演電影、歌星拍戲等太平常了,我們就用程序來表現一下多元化情形,如圖33-9所示。

圖33-9 各類明星描述
圖33-9中定義了一個抽象明星AbsStar,然后產生出各個具體類型的明星,比如電影明星FilmStar、歌星Singer,當然還可以繼續擴展下去。這里還定義了一個抽象的行為AbsAction,描述明星所具有的活動,比如演電影、唱歌等,在這種設計下,明星可以擴展,明星的活動也可以擴展,非常靈活。我們先來看明星的活動,抽象活動如代碼清單33-45所示。
代碼清單33-45 抽象活動
public?abstract?class?AbsAction?{
?????//每個活動都有描述
?????public?abstract?void?desc();
}
很簡單,只有一個活動的描述,由子類來實現。我們來看演電影和唱歌兩個活動,分別如代碼清單33-46、33-47所示。
代碼清單33-46 演電影
public?class?ActFilm?extends?AbsAction?{
?????public?void?desc()?{
?????????????System.out.println("演出精彩絕倫的電影");
?????}
}
代碼清單33-47 唱歌
public?class?Sing?extends?AbsAction?{
?????public?void?desc()?{
?????????????System.out.println("唱出優美的歌曲");
?????}
}
各種精彩的活動都有了,我們再來看抽象明星,它是所有明星的代表,如代碼清單33-48所示。
代碼清單33-48 抽象明星
public?abstract?class?AbsStar?{
?????//一個明星參加哪些活動
?????protected?final?AbsAction?action;
?????//通過構造函數傳遞具體活動
?????public?AbsStar(AbstAction?_action){
?????????????this.action?=?_action;
?????}
?????//每個明星都有自己的主要工作
?????public?void?doJob(){
?????????????action.desc();
?????}
}
明星都有自己的主要活動(或者是主要工作),我們在抽象明星中只是定義明星有活動,具體有什么活動由各個子類實現。我們再來看電影明星,如代碼清單33-49所示。
代碼清單33-49 電影明星
public?class?FilmStar?extends?AbsStar?{
?????//默認的電影明星的主要工作是拍電影
?????public?FilmStar(){
?????????????super(new?ActFilm());
?????}
?????//也可以重新設置一個新職業
?????public?FilmStar(AbsAction?_action){
?????????????super(_action);
?????}
?????//細化電影明星的職責
?????public?void?doJob(){
?????????????System.out.println("\n======影星的工作=====");
?????????????super.doJob();
?????}
}
電影明星的本職工作就應該是演電影,因此就有了一個無參構造函數來定義電影明星的默認工作,如果明星要客串一下去唱歌也可以,有參構造解決了該問題。歌星的實現與此相同,如代碼清單33-50所示。
代碼清單33-50 歌星
public?class?Singer?extends?AbsStar?{
?????//歌星的默認活動是唱歌
?????public?Singer(){
?????????????super(new?Sing());
?????}
?????//也可以重新設置一個新職業
?????public?Singer(AbsAction?_action){
?????????????super(_action);
?????}
?????//細化歌星的職責
?????public?void?doJob(){
?????????????System.out.println("\n======歌星的工作=====");
?????????????super.doJob();
?????}
}
我們使用電影明星和歌星來作為代表,這兩類明星也是我們經常聽到或看到的,下面建立一個場景類來模擬一下明星的事跡,如代碼清單33-51所示。
代碼清單33-51 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//聲明一個電影明星
?????????????AbsStar?zhangSan?=?new?FilmStar();
?????????????//聲明一個歌星
?????????????AbsStar?liSi?=?new?Singer();
?????????????//展示一下各個明星的主要工作
?????????????zhangSan.doJob();
?????????????liSi.doJob();
?????????????//當然,也有部分明星不務正業,比如歌星演戲
?????????????liSi?=?new?Singer(new?ActFilm());
?????????????liSi.doJob();
?????}
}
運行結果如下所示:
======影星的工作=====
演出精彩絕倫的電影
======歌星的工作=====
唱出優美的歌曲
======歌星的工作=====
演出精彩絕倫的電影
好了,各類明星都有自己的本職工作,但是偶爾客串一個其他類型的活動也是允許的,如此設計后,明星就可以不用固定在自己的本職工作上,而是向其他方向發展,比如影視歌三棲明星。
門面模式我們在其他章節已經講解得比較多了,本小節就不再贅述。
33.3.5 最佳實踐
5個包裝模式是大家在系統設計中經常會用到的模式,它們具有相似的特征:都是通過委托的方式對一個對象或一系列對象(例如門面模式)施行包裝,有了包裝,設計的系統才更加靈活、穩定,并且極具擴展性。從實現的角度來看,它們都是代理的一種具體表現形式,我們來看看它們在使用場景上有什么區別。
代理模式主要用在不希望展示一個對象內部細節的場景中,比如一個遠程服務不需要把遠程連接的所有細節都暴露給外部模塊,通過增加一個代理類,可以很輕松地實現被代理類的功能封裝。此外,代理模式還可以用在一個對象的訪問需要限制的場景中,比如AOP。
裝飾模式是一種特殊的代理模式,它倡導的是在不改變接口的前提下為對象增強功能,或者動態添加額外職責。就擴展性而言,它比子類更加靈活,例如在一個已經運行的項目中,可以很輕松地通過增加裝飾類來擴展系統的功能。
適配器模式的主要意圖是接口轉換,把一個對象的接口轉換成系統希望的另外一個接口,這里的系統指的不僅僅是一個應用,也可能是某個環境,比如通過接口轉換可以屏蔽外界接口,以免外界接口深入系統內部,從而提高系統的穩定性和可靠性。
橋梁模式是在抽象層產生耦合,解決的是自行擴展的問題,它可以使兩個有耦合關系的對象互不影響地擴展,比如對于使用筆畫圖這樣的需求,可以采用橋梁模式設計成用什么筆(鉛筆、毛筆)畫什么圖(圓形、方形)的方案,至于以后需求的變更,如增加筆的類型,增加圖形等,對該設計來說是小菜一碟。
門面模式是一個粗粒度的封裝,它提供一個方便訪問子系統的接口,不具有任何的業務邏輯,僅僅是一個訪問復雜系統的快速通道,沒有它,子系統照樣運行,有了它,只是更方便訪問而已。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——6大設計原則全新解讀
- 第1章 單一職責原則
- 1.2 絕殺技,打破你的傳統思維
- 1.3 我單純,所以我快樂
- 1.4 最佳實踐
- 第2章 里氏替換原則
- 2.2 糾紛不斷,規則壓制
- 2.3 最佳實踐
- 第3章 依賴倒置原則
- 3.2 言而無信,你太需要契約
- 3.3 依賴的三種寫法
- 3.4 最佳實踐
- 第4章 接口隔離原則
- 4.2 美女何其多,觀點各不同
- 4.3 保證接口的純潔性
- 4.4 最佳實踐
- 第5章 迪米特法則
- 5.2 我的知識你知道得越少越好
- 5.3 最佳實踐
- 第6章 開閉原則
- 6.2 開閉原則的廬山真面目
- 6.3 為什么要采用開閉原則
- 6.4 如何使用開閉原則
- 6.5 最佳實踐
- 第二部分 真刀實槍 ——23種設計模式完美演繹
- 第7章 單例模式
- 7.2 單例模式的定義
- 7.3 單例模式的應用
- 7.4 單例模式的擴展
- 7.5 最佳實踐
- 第8章 工廠方法模式
- 8.2 工廠方法模式的定義
- 8.3 工廠方法模式的應用
- 8.4 工廠方法模式的擴展
- 8.5 最佳實踐
- 第9章 抽象工廠模式
- 9.2 抽象工廠模式的定義
- 9.3 抽象工廠模式的應用
- 9.4 最佳實踐
- 第10章 模板方法模式
- 10.2 模板方法模式的定義
- 10.3 模板方法模式的應用
- 10.4 模板方法模式的擴展
- 10.5 最佳實踐
- 第11章 建造者模式
- 11.2 建造者模式的定義
- 11.3 建造者模式的應用
- 11.4 建造者模式的擴展
- 11.5 最佳實踐
- 第12章 代理模式
- 12.2 代理模式的定義
- 12.3 代理模式的應用
- 12.4 代理模式的擴展
- 12.5 最佳實踐
- 第13章 原型模式
- 13.2 原型模式的定義
- 13.3 原型模式的應用
- 13.4 原型模式的注意事項
- 13.5 最佳實踐
- 第14章 中介者模式
- 14.2 中介者模式的定義
- 14.3 中介者模式的應用
- 14.4 中介者模式的實際應用
- 14.5 最佳實踐
- 第15章 命令模式
- 15.2 命令模式的定義
- 15.3 命令模式的應用
- 15.4 命令模式的擴展
- 15.5 最佳實踐
- 第16章 責任鏈模式
- 16.2 責任鏈模式的定義
- 16.3 責任鏈模式的應用
- 16.4 最佳實踐
- 第17章 裝飾模式
- 17.2 裝飾模式的定義
- 17.3 裝飾模式應用
- 17.4 最佳實踐
- 第18章 策略模式
- 18.2 策略模式的定義
- 18.3 策略模式的應用
- 18.4 策略模式的擴展
- 18.5 最佳實踐
- 第19章 適配器模式
- 19.2 適配器模式的定義
- 19.3 適配器模式的應用
- 19.4 適配器模式的擴展
- 19.5 最佳實踐
- 第20章 迭代器模式
- 20.2 迭代器模式的定義
- 20.3 迭代器模式的應用
- 20.4 最佳實踐
- 第21章 組合模式
- 21.2 組合模式的定義
- 21.3 組合模式的應用
- 21.4 組合模式的擴展
- 21.5 最佳實踐
- 第22章 觀察者模式
- 22.2 觀察者模式的定義
- 22.3 觀察者模式的應用
- 22.4 觀察者模式的擴展
- 22.5 最佳實踐
- 第23章 門面模式
- 23.2 門面模式的定義
- 23.3 門面模式的應用
- 23.4 門面模式的注意事項
- 23.5 最佳實踐
- 第24章 備忘錄模式
- 24.2 備忘錄模式的定義
- 24.3 備忘錄模式的應用
- 24.4 備忘錄模式的擴展
- 24.5 最佳實踐
- 第25章 訪問者模式
- 25.2 訪問者模式的定義
- 25.3 訪問者模式的應用
- 25.4 訪問者模式的擴展
- 25.5 最佳實踐
- 第26章 狀態模式
- 26.2 狀態模式的定義
- 26.3 狀態模式的應用
- 第27章 解釋器模式
- 27.2 解釋器模式的定義
- 27.3 解釋器模式的應用
- 27.4 最佳實踐
- 第28章 享元模式
- 28.2 享元模式的定義
- 28.3 享元模式的應用
- 28.4 享元模式的擴展
- 28.5 最佳實踐
- 第29章 橋梁模式
- 29.2 橋梁模式的定義
- 29.3 橋梁模式的應用
- 29.4 最佳實踐
- 第三部分 誰的地盤誰做主 ——設計模式PK
- 第30章 創建類模式大PK
- 30.1 工廠方法模式VS建造者模式
- 30.2 抽象工廠模式VS建造者模式
- 第31章 結構類模式大PK
- 31.1 代理模式VS裝飾模式
- 31.2 裝飾模式VS適配器模式
- 第32章 行為類模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS狀態模式
- 32.3 觀察者模式VS責任鏈模式
- 第33章 跨戰區PK
- 33.1 策略模式VS橋梁模式
- 33.2 門面模式VS中介者模式
- 33.3 包裝模式群PK
- 第四部分 完美世界 ——設計模式混編
- 第34章 命令模式+責任鏈模式
- 34.2 混編小結
- 第35章 工廠方法模式+策略模式
- 35.2 混編小結
- 第36章 觀察者模式+中介者模式
- 36.2 混編小結
- 第五部分 擴展篇
- 第37章 MVC框架
- 37.2 最佳實踐
- 第38章 新模式
- 38.1 規格模式
- 38.2 對象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空對象模式
- 附錄 23種設計模式彩圖