32.3 觀察者模式VS責任鏈模式
為什么要把觀察者模式和責任鏈模式放在一起對比呢?看起來這兩個模式沒有太多的相似性,真沒有嗎?回答是有。我們在觀察者模式中也提到了觸發鏈(也叫做觀察者鏈)的問題,一個具體的角色既可以是觀察者,也可以是被觀察者,這樣就形成了一個觀察者鏈。這與責任鏈模式非常類似,它們都實現了事務的鏈條化處理,比如說在上課的時候你睡著了,打鼾聲音太大,蓋過了老師講課聲音,老師火了,捅到了校長這里,校長也處理不了,然后告狀給你父母,于是你的魔鬼日子來臨了,這是責任鏈模式,老師、校長、父母都是鏈中的一個具體角色,事件(你睡覺)在鏈中傳遞,最終由一個具體的節點來處理,并將結果反饋給調用者(你挨揍了)。那什么是觸發鏈?你還是在課堂上睡覺,還是打鼾聲音太大,老師火了,但是老師掏出個擴音器來講課,于是你睡不著了,同時其他同學的耳朵遭殃了,這就是觸發鏈,其中老師既是觀察者(相對你)也是被觀察者(相對其他同學),事件從“你睡覺”到老師這里轉化為“擴音器放大聲音”,這也是一個鏈條結構,但是鏈結構中傳遞的事件改變了。
我們還是以一個具體的例子來說明兩者的區別,DNS協議相信大家都聽說過,只要在“網絡設置”中設置一個DNS服務器地址就可以把我們需要的域名翻譯成IP地址。DNS協議還是比較簡單的,傳遞過去一個域名以及記錄標志(比如是要A記錄還是要MX記錄),DNS就開始查找自己的記錄樹,找到后把IP地址反饋給請求者。我們可以在Windows操作系統中了解一下DNS解析過程,在DOS窗口下輸入nslookup命令后,結果如圖32-6所示。

圖32-6 DNS服務器解析域名
我們的意圖就是要DNS服務器192.168.10.1解析出www.xxx.com.cn的IP地址,DNS服務器是如何工作的呢?圖32-6中的192.168.10.1這個DNS Server存儲著全球的域名和IP之間的對應關系嗎?不可能,目前全球的域名數量是1.7億個,如此龐大的數字,每個DNS服務器都存儲一份,還怎么快速響應?DNS解析的響應時間一般都是毫秒級別的,如此高的性能要求還怎么讓DNS服務器遍地開花呢?而且域名變更非常頻繁,數據讀寫的量也非常大,不可能每個DNS服務器都保留這1.7億數據,那么是怎么設計的呢?DNS協議還是很聰明的,它規定了每個區域的DNS服務器(Local DNS)只保留自己區域的域名解析,對于不能解析的域名,則提交上級域名解析器解析,最終由一臺位于美國洛杉磯的頂級域名服務器進行解析,返回結果。很明顯這是一個事務的鏈結構處理,我們使用兩種模式來實現該解析過程。
32.3.1 責任鏈模式實現DNS解析過程
本小節我們用責任鏈模式來實現DNS解析過程。首先我們定義一下業務場景,這里有三個DNS服務器:上海DNS服務器(區域服務器)、中國頂級DNS服務器(父服務器)、全球頂級DNS服務器,其示意圖如圖32-7所示。

圖32-7 DNS解析示意圖
假設有請求者發出請求,由上海DNS進行解析,如果能夠解析,則返回結果,若不能解析,則提交給父服務器(中國頂級DNS)進行解析,若還不能解析,則提交到全球頂級DNS進行解析,若還不能解析呢?那就返回該域名無法解析。確實,這與責任鏈模式非常相似,我們把這一過程抽象一下,類圖如圖32-8所示。

圖32-8 責任鏈模式實現DNS解析的類圖
我們來解釋一下類圖,Recorder是一個BO對象,它記錄DNS服務器解析后的結果,包括域名、IP地址、屬主(即由誰解析的),除此之外還有getter/setter方法。DnsServer抽象類中的resolve方法是一個基本方法,每個DNS服務器都必須擁有該方法,它對DNS進行解析,如何解析呢?具體是由echo方法來實現的,每個DNS服務器獨自實現。類圖還是比較簡單的,我們首先看一下解析記錄Recorder類,如代碼清單32-31所示。
代碼清單32-31 解析記錄
public?class?Recorder?{
?????//域名
?????private?String?domain;
?????//IP地址
?????private?String?ip;
?????//屬主
?????private?String?owner;
?????public?String?getDomain()?{
?????????????return?domain;
?????}
?????public?void?setDomain(String?domain)?{
?????????????this.domain?=?domain;
?????}
?????public?String?getIp()?{
?????????????return?ip;
?????}
?????public?void?setIp(String?ip)?{
?????????????this.ip?=?ip;
?????}
?????public?String?getOwner()?{
?????????????return?owner;
?????}
?????public?void?setOwner(String?owner)?{
?????????????this.owner?=?owner;
?????}
?????//輸出記錄信息
?????@Override
?????public?String?toString(){
?????????????String?str=?"域名:"?+?this.domain;
?????????????str?=?str?+?"\nIP地址:"?+?this.ip;
?????????????str?=?str?+?"\n解析者:"?+?this.owner;
?????????????return?str;
?????}
}
為什么要覆寫toString方法呢?是為了打印展示的需要,可以直接把Recorder的信息打印出來。我們再來看抽象域名服務器,如代碼清單32-32所示。
代碼清單32-32 抽象域名服務器
public?abstract?class?DnsServer?{
?????//上級DNS是誰
?????private?DnsServer?upperServer;
?????//解析域名
?????public?final?Recorder?resolve(String?domain){
?????????????Recorder?recorder=null;
?????????????if(isLocal(domain)){//是本服務器能解析的域名
?????????????????????recorder?=?echo(domain);
?????????????}else{//本服務器不能解析
?????????????????????//提交上級DNS進行解析
?????????????????????recorder?=?upperServer.resolve(domain);
?????????????}
?????????????return?recorder;
?????}
?????//指向上級DNS
?????public?void?setUpperServer(DnsServer?_upperServer){
?????????????this.upperServer?=?_upperServer;
?????}
?????//每個DNS都有一個數據處理區(ZONE),檢查域名是否在本區中
?????protected?abstract?boolean?isLocal(String?domain);
?????//每個DNS服務器都必須實現解析任務
?????protected?Recorder?echo(String?domain){
?????????????Recorder?recorder?=?new?Recorder();
?????????????//獲得IP地址
?????????????recorder.setIp(genIpAddress());
?????????????recorder.setDomain(domain);
?????????????return?recorder;
?????}
?????//隨機產生一個IP地址,工具類
?????private?String?genIpAddress(){
?????????????Random?rand?=?new?Random();
?????????????String?address?=?rand.nextInt(255)?+?"."?+?rand.nextInt(255)?+?"."+?rand.nextInt(255)?+?"."+?rand.nextInt(255);
?????????????return?address;
?????}
}
在該類中有一個方法——genIpAddress方法——沒有在類圖中展現出來,它用于實現隨機生成IP地址,這是我們為模擬DNS解析場景而建立的一個虛擬方法,在實際的應用中是不可能出現的。抽象DNS服務器編寫完成,我們再來看具體的DNS服務器,先看上海的DNS服務器,如代碼清單32-33所示。
代碼清單32-33 上海DNS服務器
public?class?SHDnsServer?extends?DnsServer?{
?????@Override
?????protected?Recorder?echo(String?domain)?{
?????????????Recorder?recorder=?super.echo(domain);
?????????????recorder.setOwner("上海DNS服務器");
?????????????return?recorder;
?????}
?????//定義上海的DNS服務器能處理的級別
?????@Override
?????protected?boolean?isLocal(String?domain)?{
?????????????return?domain.endsWith(".sh.cn");
?????}
}
為什么要覆寫echo方法?各具體的DNS服務器實現自己的解析過程,屬于個性化處理,它代表的是每個DNS服務器的不同處理邏輯。還要注意一下,我們在這里做了一個簡化處理,所有以".sh.cn"結尾的域名都由上海DNS服務器解析。其他的中國頂級DNS和全球頂級DNS實現過程類似,如代碼清單32-34、32-35所示。
代碼清單32-34 中國頂級DNS服務器
public?class?ChinaTopDnsServer?extends?DnsServer?{
?????@Override
?????protected?Recorder?echo(String?domain)?{
????????????????Recorder?recorder?=?super.echo(domain);
????????????????recorder.setOwner("中國頂級DNS服務器");
????????????????return?recorder;
?????}
?????@Override
?????protected?boolean?isLocal(String?domain)?{
????????????????return?domain.endsWith(".cn");
?????}
}
代碼清單32-35 全球頂級DNS服務器
public?class?TopDnsServer?extends?DnsServer?{
?????@Override
?????protected?Recorder?echo(String?domain)?{
?????????????Recorder?recorder?=?super.echo(domain);
?????????????recorder.setOwner("全球頂級DNS服務器");
?????????????return?recorder;
?????}
?????@Override
?????protected?boolean?isLocal(String?domain)?{
?????????????//所有的域名最終的解析地點
?????????????return?true;
?????}
}
所有的DNS服務器都準備好了,下面我們寫一個客戶端來模擬一下IP地址是怎么解析的,如代碼清單32-36所示。
代碼清單32-36 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?throws?Exception?{
?????????????//上海域名服務器
?????????????DnsServer?sh?=?new?SHDnsServer();
?????????????//中國頂級域名服務器
?????????????DnsServer?china?=?new?ChinaTopDnsServer();
?????????????//全球頂級域名服務器
?????????????DnsServer?top?=?new?TopDnsServer();
?????????????//定義查詢路徑
?????????????china.setUpperServer(top);
?????????????sh.setUpperServer(china);
?????????????//解析域名
?????????????System.out.println("=====域名解析模擬器=====");
?????????????while(true){
?????????????????????System.out.print("\n請輸入域名(輸入N退出):");
?????????????????????String?domain?=?(new?BufferedReader(new?InputStreamReader?(System.in))).readLine();
?????????????????????if(domain.equalsIgnoreCase("n")){
?????????????????????????????return;
?????????????????????}
?????????????????????Recorder?recorder?=?sh.resolve(domain);
?????????????????????System.out.println("----DNS服務器解析結果----");
?????????????????????System.out.println(recorder);???????????????
?????????????}
?????}
}
我們來模擬一下,運行結果如下所示:
=====域名解析模擬器=====
請輸入域名(輸入N退出):www.xxx.sh.cn
----DNS服務器解析結果----
域名:www. xxx.sh.cn
IP地址:69.224.162.154
解析者:上海DNS服務器
請輸入域名(輸入N退出):www. xxx.com.cn
----DNS服務器解析結果----
域名:www. xxx.com.cn
IP地址:51.28.66.140
解析者:中國頂級DNS服務器
請輸入域名(輸入N退出):www. xxx.com
----DNS服務器解析結果----
域名:www. xxx.com
IP地址:73.247.80.117
解析者:全球頂級DNS服務器
請輸入域名(輸入N退出):n
請注意看運行結果,以".sh.cn"結尾的域名確實由上海DNS服務器解析了,以".cn"結尾的域名由中國頂級DNS服務器解析了,其他域名都由全球頂級DNS服務器解析。這個模擬過程看起來很完整,它完全就是責任鏈模式的一個具體應用,把一個請求放置到鏈中的首節點,然后由鏈中的某個節點進行解析并將結果反饋給調用者。但是,我可以負責任地告訴你:這個解析過程是有缺陷的,什么缺陷?后面會說明。
32.3.2 觸發鏈模式實現DNS解析過程
上面說到使用責任鏈模式模擬DNS解析過程是有缺陷的,究竟有什么缺陷?大家是不是覺得這個解析過程很完美了,沒什么問題了?那說明你對DNS協議了解得還不太深入。我們來做一個實驗,在dos窗口下輸入nslookup命令,然后輸入多個域名,注意觀察返回值有哪些數據是相同的。可以看出,解析者都相同,都是由同一個DNS服務器解析的,準確地說都是由本機配置的DNS服務器做的解析。這與我們上面的模擬過程是不相同的,看看我們模擬的過程,對請求者來說,".sh.cn"是由區域DNS解析的,".com"卻是由全球頂級DNS解析的,與真實的過程不相同,這是怎么回事呢?
肯定地說,采用責任鏈模式模擬DNS解析過程是不完美的,或者說是有缺陷的,怎么來修復這個缺陷呢?我們先來看看真實的DNS解析過程,如圖32-9所示。

圖32-9 真實的DNS解析示意圖
解析一個域名的完整路徑如圖32-9中的標號①~⑥所示,首先由請求者發送一個請求,然后由上海DNS服務器嘗試解析,若不能解析再通過路徑②轉發給中國頂級DNS進行解析,解析后的結果通過路徑⑤返回給上海DNS服務器,然后由上海DNS服務器通過路徑⑥返回給請求者。同樣,若中國頂級DNS不能解析,則通過路徑③轉由全球頂級DNS進行解析,通過路徑④把結果返回給中國頂級DNS,然后再通過路徑⑤返回給上海DNS。注意看標號⑥,不管一個域名最終由誰解析,最終反饋到請求者的還是第一個節點,也就是說首節點負責對請求者應答,其他節點都不與請求者交互,而只與自己的左右節點交互。實際上我們的DNS服務器確實是如此處理的,例如本機請求查詢一個www.abcdefg.com的域名,上海DNS服務器解析不到這個域名,于是提交到中國頂級DNS服務器,如果中國頂級DNS服務器有該域名的記錄,則找到該記錄,反饋到上海DNS服務器,上海DNS服務器做兩件事務處理:一是響應請求者,二是存儲該記錄,以備其他請求者再次查詢,這類似于數據緩存。
整個場景我們已經清晰,想想看,我們把請求者看成是被觀察者,它的行為或屬性變更通知了觀察者——上海DNS,上海DNS又作為被觀察者出現了自己不能處理的行為(行為改變),通知了中國頂級DNS,依次類推,這是不是一個非常標準的觸發鏈?而且還必須是同步的觸發,異步觸發已經在該場景中失去了意義(讀者可以想想為什么)。
分析了這么多,我們用觸發鏈來模擬DNS的解析過程,如圖32-10所示。

圖32-10 觸發鏈模式實現DNS解析的類圖
與責任鏈模式很相似,僅僅多了一個Observable父類和Observer接口,但是在實現上這兩種模式有非常大的差異。我們先來解釋一下抽象DnsServer的作用。
● 標示聲明
表示所有的DNS服務器都具備雙重身份:既是觀察者也是被觀察者,這很重要,它聲明所有的服務器都具有相同的身份標志,具有該標志后就可以在鏈中隨意移動,而無需固定在鏈中的某個位置(這也是鏈的一個重要特性)。
● 業務抽象
方法setUpperServer的作用是設置父DNS,也就是設置自己的觀察者,update方法不僅僅是一個事件的處理者,也同時是事件的觸發者。
我們來看代碼,首先是最簡單的,Recorder類與責任鏈模式中的記錄相同,這里不再贅述。那我們就先看看該模式的核心抽象DnsServer,如代碼清單32-37所示。
代碼清單32-37 抽象DNS服務器
public?abstract?class?DnsServer?extends?Observable?implements?Observer?{
?????//處理請求,也就是接收到事件后的處理
?????public?void?update(Observable?arg0,?Object?arg1)?{
?????????????Recorder?recorder?=?(Recorder)arg1;
?????????????//如果本機能解析
?????????????if(isLocal(recorder)){
?????????????????????recorder.setIp(genIpAddress());
?????????????}else{//本機不能解析,則提交到上級DNS
?????????????????????responsFromUpperServer(recorder);
?????????????}
?????????????//簽名
?????????????sign(recorder);
?????}
?????//作為被觀察者,允許增加觀察者,這里上級DNS一般只有一個
?????public?void?setUpperServer(DnsServer?dnsServer){
?????????????//先清空,然后再增加
?????????????super.deleteObservers();
?????????????super.addObserver(dnsServer);
?????}
?????//向父DNS請求解析,也就是通知觀察者
?????private?void?responsFromUpperServer(Recorder?recorder){
?????????????super.setChanged();
?????????????super.notifyObservers(recorder);
?????}
?????//每個DNS服務器簽上自己的名字
?????protected?abstract?void?sign(Recorder?recorder);
?????//每個DNS服務器都必須定義自己的處理級別
?????protected?abstract?boolean?isLocal(Recorder?recorder);
?????//隨機產生一個IP地址,工具類
?????private?String?genIpAddress(){
?????????????Random?rand?=?new?Random();
?????????????String?address?=?rand.nextInt(255)?+?"."?+?rand.nextInt(255)?+?"."+?rand.nextInt(255)?+?"."+?rand.nextInt(255);
?????????????return?address;
?????}
}
注意看一下responseFromUpperServer方法,它只允許設置一個觀察者,因為一般的DNS服務器都只有一個上級DNS服務器。sign方法是簽名,這個記錄是由誰解析出來的,就由各個實現類獨自來實現。三個DnsServer的實現類都比較簡單,如代碼清單32-38、32-39、32-40所示。
代碼清單32-38 上海DNS服務器
public?class?SHDnsServer?extends?DnsServer?{
?????@Override
?????protected?void?sign(Recorder?recorder)?{
?????????????recorder.setOwner("上海DNS服務器");
?????}
?????//定義上海的DNS服務器能處理的級別
?????@Override
?????protected?boolean?isLocal(Recorder?recorder)?{
?????????????return?recorder.getDomain().endsWith(".sh.cn");
?????}
}
代碼清單32-39 中國頂級DNS服務器
public?class?ChinaTopDnsServer?extends?DnsServer?{
?????@Override
?????protected?void?sign(Recorder?recorder)?{
?????????????recorder.setOwner("中國頂級DNS服務器");
?????}
?????@Override
?????protected?boolean?isLocal(Recorder?recorder)?{
?????????????return?recorder.getDomain().endsWith(".cn");
?????}
}
代碼清單32-40 全球頂級DNS服務器
public?class?TopDnsServer?extends?DnsServer?{
?????@Override
?????protected?void?sign(Recorder?recorder)?{
?????????????recorder.setOwner("全球頂級DNS服務器");
?????}
?????
?????@Override
?????protected?boolean?isLocal(Recorder?recorder)?{
?????????????//所有的域名最終的解析地點
?????????????return?true;
?????}
}
我們再建立一個場景類模擬一下DNS解析過程,如代碼清單32-41所示。
代碼清單32-41 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?throws?Exception?{
?????????????//上海域名服務器
?????????????DnsServer?sh?=?new?SHDnsServer();
?????????????//中國頂級域名服務器
?????????????DnsServer?china?=?new?ChinaTopDnsServer();
?????????????//全球頂級域名服務器
?????????????DnsServer?top?=?new?TopDnsServer();
?????????????//定義查詢路徑
?????????????china.setUpperServer(top);
?????????????sh.setUpperServer(china);
?????????????//解析域名
?????????????System.out.println("=====域名解析模擬器=====");
?????????????while(true){
?????????????????????System.out.print("\n請輸入域名(輸入N退出):");
?????????????????????String?domain?=?(new?BufferedReader(new?InputStreamReader?(System.in))).readLine();
?????????????????????if(domain.equalsIgnoreCase("n")){
?????????????????????????????return;
?????????????????????}
?????????????????????Recorder?recorder?=?new?Recorder();
?????????????????????recorder.setDomain(domain);
?????????????????????sh.update(null,recorder);
?????????????????????System.out.println("----DNS服務器解析結果----");
?????????????????????System.out.println(recorder);???????????????
?????????????}
?????}
}
與責任鏈模式中的場景類很相似。讀者請注意sh.update(null,recorder)這句代碼,這是我們虛擬了觀察者觸發動作,完整的做法是把場景類作為一個被觀察者,然后設置觀察者為上海DNS服務器,再進行測試,其結果完全相同,我們這里為減少代碼量采用了簡化處理,有興趣的讀者可以擴充實現。
我們來看看運行結果如何,結果如下所示:
=====域名解析模擬器=====
請輸入域名(輸入N退出):www.xxx.sh.cn
----DNS服務器解析結果----
域名:www.xxx.sh.cn
IP地址:197.15.34.227
解析者:上海DNS服務器
請輸入域名(輸入N退出):www.xxx.com.cn
----DNS服務器解析結果----
域名:www.xxx.com.cn
IP地址:201.177.148.99
解析者:上海DNS服務器
請輸入域名(輸入N退出):www.xxx.com
----DNS服務器解析結果----
域名:www.xxx.com
IP地址:251.41.14.230
解析者:上海DNS服務器
請輸入域名(輸入N退出):n
可以看出,所有的解析結果都是由上海DNS服務器返回的,這才是真正的DNS解析過程。如何知道它是由上海DNS服務器解析的還是由別的DNS服務器解析的呢?很好辦,把代碼拷貝過去,然后調試跟蹤一下就可以了。或者仔細看看代碼,理解一下代碼邏輯也可以非常清楚地知道它是如何解析的。
再仔細看一下我們的代碼邏輯,上下兩個節點之間的關系很微妙,很有意思。
● 下級節點對上級節點頂禮膜拜
比如我們輸入的這個域名www.xxx.com,上海域名服務器只知道它是由父節點(中國頂級DNS服務器)解析的,而不知道父節點把該請求轉發給了更上層節點(全球頂級DNS服務器),也就是說下級節點關注的是上級節點的響應,只要是上級反饋的結果就認為是上級的。www.xxx.com這個域名最終是由最高節點(全球頂級DNS服務器)解析的,它把解析結果傳遞給第二個節點(中國頂級DNS服務器)時的簽名為“全球頂級DNS服務器”,而第二個節點把請求傳遞給首節點(上海DNS服務器)時的簽名被修改為“中國頂級DNS服務器”。所有從上級節點反饋的響應都認為是上級節點處理的結果,而不追究到底是不是真的是上級節點處理的。
● 上級節點對下級節點絕對信任
上級節點只對下級節點負責,它不關心下級節點的請求從何而來,只要是下級發送的請求就認為是下級的。還是以www.xxx.com域名為例,當最高節點(全球頂級DNS服務器)獲得解析請求時,它認為這個請求是誰的?當然是第二個節點(中國頂級DNS服務器)的,否則它也不會把結果反饋給它,但是這個請求的源頭卻是首節點(上海DNS服務器)的。
32.3.3 小結
通過對DNS解析過程的實現,我們發現觸發鏈和責任鏈雖然都是鏈結構,但是還是有區別的。
● 鏈中的消息對象不同
從首節點開始到最終的尾節點,兩個鏈中傳遞的消息對象是不同的。責任鏈模式基本上不改變消息對象的結構,雖然每個節點都可以參與消費(一般是不參與消費),類似于“雁過拔毛”,但是它的結構不會改變,比如從首節點傳遞進來一個String對象或者Person對象,不會到鏈尾的時候成了int對象或者Human對象,這在責任鏈模式中是不可能的,但是在觸發鏈模式中是允許的,鏈中傳遞的對象可以自由變化,只要上下級節點對傳遞對象了解即可,它不要求鏈中的消息對象不變化,它只要求鏈中相鄰兩個節點的消息對象固定。
● 上下節點的關系不同
在責任鏈模式中,上下節點沒有關系,都是接收同樣的對象,所有傳遞的對象都是從鏈首傳遞過來,上一節點是什么沒有關系,只要按照自己的邏輯處理就成。而觸發鏈模式就不同了,它的上下級關系很親密,下級對上級頂禮膜拜,上級對下級絕對信任,鏈中的任意兩個相鄰節點都是一個牢固的獨立團體。
● 消息的分銷渠道不同
在責任鏈模式中,一個消息從鏈首傳遞進來后,就開始沿著鏈條向鏈尾運動,方向是單一的、固定的;而觸發鏈模式則不同,由于它采用的是觀察者模式,所以有非常大的靈活性,一個消息傳遞到鏈首后,具體怎么傳遞是不固定的,可以以廣播方式傳遞,也可以以跳躍方式傳遞,這取決于處理消息的邏輯。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——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種設計模式彩圖