第36章 觀察者模式+中介者模式
36.1 事件觸發器的開發
大家都應該做過桌面程序的開發吧,比如編寫一個EXE文件,或者使用Java Swing編寫一個應用程序,或者是用Delphi、C編寫C/S結構的應用系統,即使這些都沒有做過,那也總編寫過B/S結構的頁面吧?回憶一下開發過程,大家是不是經常使用文本框和按鈕這兩個控件?比如設計一個按鈕,那總要編寫鼠標點擊處理,你是不是這樣開發:在按鈕的onClick函數中編寫自己的邏輯代碼,然后鼠標點擊測試,該代碼就會運行。大家有沒有想過為什么我們點擊了按鈕就會觸發我們自己編寫的代碼呢?瀏覽器怎么知道操作者按了按鈕要觸發該事件呢?鼠標點擊動作、按鈕、自己編寫的代碼之間是如何關聯起來呢?
我們今天的任務就是來模擬類似觸發過程。我們這樣分析:有一個產品(不管是Frame還是Button或者是Radio),它有多個觸發事件,它產生的時候觸發一個創建事件,修改的時候觸發修改事件,刪除的時候觸發刪除事件,這就類似于我們的文本框,初始化(也就是創建)的時候要觸發一個onLoad或onCreate事件,修改的時候觸發onChange事件,雙擊(類似于刪除)的時候又觸發onDbClick事件,我們今天的目標就是來思考怎么實現這樣一個架構。
設計都是先易后難,我們先從最簡單的部分入手。首先需要一個產品,并且該產品要有創建、修改、銷毀的動作,很明顯這就是一個工廠方法模式。同時產品也可以通過克隆方式產生,這與我們在GUI設計中經常使用的復制粘貼操作相類似,要不界面上那么多的文本框,不使用復制粘貼,不累死人才怪呢,那這非常明顯就是原型模式。好,分析到這里,我們先把這部分的類圖建立起來,如圖36-1所示。
很熟悉的類圖,與工廠方法模式的通用類圖非常相似,但不完全是。有什么差別呢?注意看產品類的私有屬性canChanged和構造函數,它們有特殊的用途。在該類圖中,我們使用了工廠方法模式創建產品,使用原型模式讓對象可以被拷貝,僅僅這兩個模式還不足以解決我們的問題,想想看,產品的產生是有一定的條件的,不是誰想產生就產生,否則怎么能夠觸發創建事件呢?因此需要限定產品的創建者,所以我們在類圖中把產品和工廠的關系定位為組合關系,而不是簡單的聚集或依賴關系。換句話說,產品只能由工廠類創建,而不能被其他對象通過new方式創建,因此我們在這里還用到一個單來源調用(Single Call)方法解決該問題。這是一個方法,不是一個設計模式,我馬上給大家講解它是如何工作的。

圖36-1 產品創建工廠
我們先來看產品類的源代碼,它比較簡單,如代碼清單36-1所示。
代碼清單36-1 產品類
public?class?Product?implements?Cloneable{
?????//產品名稱
?????private?String?name;
?????//是否可以屬性變更
?????private?boolean?canChanged?=?false;
?????//產生一個新的產品
?????public?Product(ProductManager?manager,String?_name){
?????????????//允許建立產品
?????????????if(manager.isCreateProduct()){
?????????????????????canChanged?=true;
?????????????????????this.name?=?_name;
?????????????}
?????}
?????public?String?getName()?{
?????????????return?name;
?????}
?????public?void?setName(String?name)?{
?????????????if(canChanged){
?????????????????????this.name?=?name;
?????????????}
?????}
?????//覆寫clone方法
?????@Override
?????public?Product?clone(){
?????????????Product?p?=null;
?????????????try?{
?????????????????????p?=(Product)super.clone();
?????????????}?catch?(CloneNotSupportedException?e)?{
?????????????????????e.printStackTrace();
?????????????}
?????????????return?p;
?????}
}
在產品類中,我們只定義產品的一個屬性:產品名稱(name),并實現了getter/setter方法,然后我們實現了它的clone方法,確保對象是可以被拷貝的。還有一個特殊的地方是我們的構造函數,它怎么會要求傳遞進來一個工廠對象ProductManager呢?保留你的好奇心,馬上為你揭曉答案。我們繼續看代碼,工廠類如代碼清單36-2所示。
代碼清單36-2 工廠類
public?class?ProductManager?{
?????//是否可以創建一個產品
?????private?boolean?isPermittedCreate?=?false;
?????//建立一個產品
?????public?Product?createProduct(String?name){
?????????????//首先修改權限,允許創建
?????????????isPermittedCreate?=?true;
?????????????Product?p?=?new?Product(this,name);
?????????????return?p;
?????}
?????//廢棄一個產品
?????public?void?abandonProduct(Product?p){
?????????????//銷毀一個產品,例如刪除數據庫記錄
?????????????p?=?null;
?????}
?????//修改一個產品
?????public?void?editProduct(Product?p,String?name){
?????????????//修改后的產品
?????????????p.setName(name);
?????}
?????//獲得是否可以創建一個產品
?????public?boolean?isCreateProduct(){
?????????????return?isPermittedCreate;
?????}
?????//克隆一個產品
?????public?Product?clone(Product?p){
?????????????//產生克隆事件
?????????????return?p.clone();
?????}
}
仔細看看工廠類,產品的創建、修改、遺棄、克隆方法都很簡單,但有一個方法可不簡單——isCreateProduct方法,它的作用是告訴產品類“我是能創建產品的”,注意看我們的程序,在工廠類ProductManager中定義了一個私有變量isCreateProduct,該變量只有在工廠類的createProduct函數中才能設置為true,在創建產品的時候,產品類Product的構造函數要求傳遞工廠對象,然后判斷是否能夠創建產品,即使你想使用類似這樣的方法:
Product?p?=?new?Product(new?ProductManager(),"abc");
也是不可能創建出產品的,它在產品類中限制必須是當前有效工廠才能生產該產品,而且也只有有效的工廠才能修改產品,看看產品類的canChanged屬性,只有它為true時,產品才可以修改,那怎么才能為true呢?在構造函數中判斷是否可以為true。這就類似工廠要創建產品了,產品就問“你有權利創建我嗎?”于是工廠類出示了兩個證明材料證明自己可以創建產品:一是“我是你的工廠類”,二是“我的isCreateProduct返回true,我有權創建”,于是產品就被創建出來了。這種一個對象只能由固定的對象初始化的方法就叫做單來源調用(Single Call)——很簡單,但非常有用的方法。
注意 采用單來源調用的兩個對象一般是組合關系,兩者有相同的生命期,它通常適用于有單例模式和工廠方法模式的場景中。
我們繼續往下分析,一個產品新建要觸發事件,那事件是什么?當然也是一個對象了,需要把它設計出來,僅僅有事件還不行,還要考慮有人去處理這個事件,產生了一個事件不可能沒有對象去處理吧?如果是這樣那事件還有什么意義呢?既然要去處理,那就需要一個通知渠道了,于是觀察者模式準備好了。好,我們把這段分析的類圖也畫出來,如圖36-2所示。

圖36-2 觀察者模式處理事件
在該類圖中,觀察者為EventDispatch類,它使用了單例模式,避免對象膨脹,但同時也帶來了性能及線程安全隱患,這點需要大家在實際應用中注意(想想Spring中的Bean注入,默認也是單例,在通常的應用中一般不需要修改,除非是較大并發的應用)。我們來看代碼,先來看事件類型定義,它是一個枚舉類型,如代碼清單36-3所示。
代碼清單36-3 事件類型定義
public?enum?ProductEventType?{
?????//新建一個產品
?????NEW_PRODUCT(1),
?????//刪除一個產品
?????DEL_PRODUCT(2),
?????//修改一個產品
?????EDIT_PRODUCT(3),
?????//克隆一個產品
?????CLONE_PRODUCT(4);
?????private?int?value=0;
?????private?ProductEventType(int?_value){
?????????????this.value?=?_value;
?????}
?????public?int?getValue(){
?????????????return?this.value;
?????}
}
這里定義了4個事件類型,分別是新建、修改、刪除以及克隆,比較簡單。我們再來看產品的事件,如代碼清單36-4所示。
代碼清單36-4 產品事件
public?class?ProductEvent?extends?Observable{
?????//事件起源
?????private?Product?source;
?????//事件的類型
?????private?ProductEventType?type;
?????//傳入事件的源頭,默認為新建類型
?????public?ProductEvent(Product?p)?{
?????????????this(p,ProductEventType.NEW_PRODUCT);
?????}
?????//事件源頭以及事件類型
?????public?ProductEvent(Product?p,ProductEventType?_type){
?????????????this.source?=?p;
?????????????this.type?=?_type;
?????????????//事件觸發
?????????????notifyEventDispatch();
?????}
?????//獲得事件的始作俑者
?????public?Product?getSource(){
?????????????return?source;
?????}
?????//獲得事件的類型
?????public?ProductEventType?getEventType(){
?????????????return?this.type;
?????}
?????//通知事件處理中心
?????private?void?notifyEventDispatch(){
?????????????super.addObserver(EventDispatch.getEventDispatch());
?????????????super.setChanged();
?????????????super.notifyObservers(source);
?????}
}
我們在產品事件類中增加了一個私有方法notfiyEventDispatch,該方法的作用是明確事件的觀察者,并同時在初始化時通知觀察者,它在有參構造中被調用。我們再來看事件的觀察者,如代碼清單36-5所示。
代碼清單36-5 事件的觀察者
public?class?EventDispatch?implements?Observer{
?????//單例模式
?????private?final?static?EventDispatch?dispatch?=?new?EventDispatch();
?????//不允許生成新的實例
?????private?EventDispatch(){
?????}
?????//獲得單例對象
?????public?static?EventDispatch?getEventDispatch(){
?????????????return?dispatch;
?????}
?????//事件觸發
?????public?void?update(Observable?o,?Object?arg)?{
?????}
}
產品和事件都定義出來了,那我們想想怎么把這兩者關聯起來,產品和事件是兩個獨立的對象,兩者都可以獨立地擴展,用什么來適應它們的擴展呢?橋梁模式!兩個不相關的類可以通過橋梁模式組合出穩定、健壯的結構,我們畫出類圖,如圖36-3所示。

圖36-3 橋梁模式實現產品和事件的組合
看著不像橋梁模式?看看橋梁模式的通用類圖,然后把抽象化角色和實現化角色去掉看看,是不是就是一樣了?各位可能要說了,把抽象化角色和實現化角色去掉,那橋梁模式在抽象層次耦合的優點還怎么體現呢?因為我們采用的是單個產品對象,沒有必要進行抽象化處理,讀者若要按照該框架做擴展開發,該部分是肯定需要抽象出接口或抽象類的,好在也非常簡單,只要抽取一下就可以了。這樣考慮后,我們的ProductManager類就增加一個功能:組合產品類和事件類,產生有意義的產品事件,如代碼清單36-6所示。
代碼清單36-6 修正后的產品工廠類
public?class?ProductManager?{
?????//是否可以創建一個產品
?????private?boolean?isPermittedCreate?=?false;
?????//建立一個產品
?????public?Product?createProduct(String?name){
?????????????//首先修改權限,允許創建
?????????????isPermittedCreate?=?true;
?????????????Product?p?=?new?Product(this,name);
?????????????//產生一個創建事件
?????????????new?ProductEvent(p,ProductEventType.NEW_PRODUCT);
?????????????return?p;
?????}
?????//廢棄一個產品
?????public?void?abandonProduct(Product?p){
?????????????//銷毀一個產品,例如刪除數據庫記錄
?????????????//產生刪除事件
?????????????new?ProductEvent(p,ProductEventType.DEL_PRODUCT);
?????????????p?=?null;
?????}
?????//修改一個產品
?????public?void?editProduct(Product?p,String?name){
?????????????//修改后的產品
?????????????p.setName(name);
?????????????//產生修改事件
?????????????new?ProductEvent(p,ProductEventType.EDIT_PRODUCT);
?????}
?????//獲得是否可以創建一個產品
?????public?boolean?isCreateProduct(){
?????????????return?isPermittedCreate;
?????}
?????//克隆一個產品
?????public?Product?clone(Product?p){
?????????????//產生克隆事件
?????????????new?ProductEvent(p,ProductEventType.CLONE_PRODUCT);
?????????????return?p.clone();
?????}
}
在每個方法中增加了事件的產生機制,在createProduct方法中增加了創建產品事件,在editProduct方法中增加了修改產品事件,在delProduct方法中增加了遺棄產品事件,在clone方法中增加克隆產品事件,而且每個事件都是通過組合產生的,產品和事件的擴展性非常優秀。
剛剛我們說完了產品和事件的關系處理,現在回到我們事件的觀察者,它承擔著非常重要的職責。我們知道它要處理事件,但是現在還沒有想好怎么實現它處理事件的update方法,暫時保持為空。
我們繼續分析,這么多的事件(現在只有1個產品類,如果產品類很多呢?比如30多個)不可能每個產品事件都寫一個處理者吧,對于產品事件來說,它最希望的結果就是我通知了事件處理者(也就是觀察者模式的觀察者),其他具體怎么處理由觀察者來解決,那現在問題是觀察者怎么來處理這么多的事件呢?事件的處理者必然有N多個,如何才能通知相應的處理者來處理事件呢?一個事件也可能通知多個處理者來處理,并且一個處理者處理完畢還可能通知其他的處理者,這不可能讓每個處理者獨自完成這樣“不可能完成的任務”,我們把問題的示意圖畫出來,如圖36-4所示。

圖36-4 事件處理示意圖
看到該示意圖,你立刻就會想到中介者模式。是的,需要中介者模式上場了,我們把EventDispatch類(嘿嘿,為什么要定義成Dispatch呢?就是分發的意思)作為事件分發的中介者,事件的處理者都是具體的同事類,它們有著相似的行為,都是處理產品事件,但是又有不相同的邏輯,每個同事類對事件都有不同的處理行為。我們來看類圖,如圖36-5所示。
在類圖中,EventDispatch類有3個職責。
● 事件的觀察者
作為觀察者模式中的觀察者角色,接收被觀察期望完成的任務,在我們的框架中就是接收ProductEvent事件。
● 事件分發者
作為中介者模式的中介者角色,它擔當著非常重要的任務——分發事件,并同時協調各個同事類(也就是事件的處理者)處理事件。
● 事件處理者的管理員角色
不是每一個事件的處理者都可以接收事件并進行處理,是需要獲得分發者許可后才可以,也就是說只有事件分發者允許它處理,它才能處理。

圖36-5 采用中介者模式對事件進行分發
事件分發者擔當了這么多的職責,那是不是與單一職責原則相違背了?確實如此,我們在整個系統的設計中確實需要這樣一個角色擔任這么多的功能,如果強制細分也可以完成,但是會加大代碼量,同時導致系統的結構復雜,讀者可以考慮拆分這3個職責,然后再組合相關的功能,看看代碼量是如何翻倍的。
注意 設計原則只是一個理論,而不是一個帶有刻度的標尺,因此在系統設計中不應該把它視為不可逾越的屏障,而是應該把它看成是一個方向標,盡量遵守,而不是必須恪守。
既然事件分發者這么重要,我們就仔細研讀一下它的代碼,如代碼清單36-7所示。
代碼清單36-7 事件分發者
public?class?EventDispatch?implements?Observer{
?????//單例模式
?????private?final?static?EventDispatch?dispatch?=?new?EventDispatch();
?????//事件消費者
?????private?Vector<EventCustomer>?customer?=?new?Vector<EventCustomer>();
?????//不允許生成新的實例
?????private?EventDispatch(){
?????}
?????//獲得單例對象
?????public?static?EventDispatch?getEventDispatch(){
?????????????return?dispatch;
?????}
?????//事件觸發
?????public?void?update(Observable?o,?Object?arg)?{
?????????????//事件的源頭
?????????????Product?product?=?(Product)arg;
?????????????//事件
?????????????ProductEvent?event?=?(ProductEvent)o;
?????????????//處理者處理,這里是中介者模式的核心,可以是很復雜的業務邏輯
?????????????for(EventCustomer?e:customer){???????????????
?????????????????????//處理能力是否匹配
?????????????????????for(EventCustomType?t:e.getCustomType()){
?????????????????????????????if(t.getValue()==?event.getEventType().getValue()){
??????????????????????????????????e.exec(event);
?????????????????????????????}
?????????????????????}
?????????????}
?????}
?????//注冊事件處理者
?????public?void?registerCustomer(EventCustomer?_customer){
?????????????customer.add(_customer);
?????}
}
我們在這里使用Vector來存儲所有的事件處理者,在update方法中使用了兩個簡單的for循環來完成業務邏輯的判斷,只要事件的處理者級別和事件的類型相匹配,就調用事件處理者的exec方法來處理事件,該邏輯是整個事件觸發架構的關鍵點,但不是難點。請讀者注意,在設計這樣的框架前,一定要定義好消費者與生產者之間的搭配問題,一般的做法是通過xml文件類或者IoC容器配置規則,然后在框架啟動時加載并駐留內存。
EventCustomer抽象類負責定義事件處理者必須具有的行為,首先是每一個事件的處理者都必須定義自己能夠處理的級別,也就是通過構造函數來定義自己的處理能力,當然處理能力可以是多值的,也就是說一個處理者可以處理多個事件;然后各個事件的處理者只要實現exec方法就可以了,完成自己對事件的消費處理即可。我們先來看抽象的事件處理者,如代碼清單36-8所示。
代碼清單36-8 抽象的事件處理者
public?abstract?class?EventCustomer?{
?????//容納每個消費者能夠處理的級別
?????private?Vector<EventCustomType>?customType?=?new?Vector<EventCustomType>();
?????//每個消費者都要聲明自己處理哪一類別的事件
?????public?EventCustomer(EventCustomType?_type){
?????????????addCustomType(_type);
?????}
?????//每個消費者可以消費多個事件
?????public?void?addCustomType(EventCustomType?_type){
?????????????customType.add(_type);
?????}
?????//得到自己的處理能力
?????public?Vector<EventCustomType>?getCustomType(){
?????????????return?customType;
?????}
?????//每個事件都要對事件進行聲明式消費
?????public?abstract?void?exec(ProductEvent?event);
}
很簡單,我們定義了一個Vector變量來存儲處理者的處理能力,然后通過構造函數約束子類必須定義一個自己的處理能力。在代碼中,我們用到了事件處理類型枚舉,如代碼清單36-9所示。
代碼清單36-9 事件處理枚舉
public?enum?EventCustomType?{
?????//新建立事件
?????NEW(1),
?????//刪除事件
?????DEL(2),
?????//修改事件
?????EDIT(3),
?????//克隆事件
?????CLONE(4);
?????private?int?value=0;
?????private?EventCustomType(int?_value){
?????????????this.value?=?_value;
?????}
?????public?int?getValue(){
?????????????return?value;
?????}
}
我們在系統中定義了3個事件處理者,分別是乞丐、平民和貴族。乞丐只能獲得別人遺棄的物品,平民消費自己生產的東西,自給自足,而貴族則可以獲得精修的產品或者是綠色產品(也就是我們這里的克隆產品,不用自己勞動獲得的產品)。我們先看乞丐的源代碼,如代碼清單36-10所示。
代碼清單36-10 乞丐
public?class?Beggar?extends?EventCustomer?{
?????//只能處理被人遺棄的東西
?????public?Beggar(){
?????????????super(EventCustomType.DEL);
?????}
?????@Override
?????public?void?exec(ProductEvent?event)?{
?????????????//事件的源頭
?????????????Product?p?=?event.getSource();
?????????????//事件類型
?????????????ProductEventType?type?=?event.getEventType();
?????????????System.out.println("乞丐處理事件:"+p.getName()?+"銷毀,事件類型="+type);
?????}
}
乞丐在無參構造中定義了自己只能處理刪除的事件,然后在exec方法中定義了事件的處理邏輯,每個處理者都是只要完成這兩個方法即可,我們再來看平民級別的事件處理者,如代碼清單36-11所示。
代碼清單36-11 平民
public?class?Commoner?extends?EventCustomer?{
?????//定義平民能夠處理的事件的級別
?????public?Commoner()?{
?????????????super(EventCustomType.NEW);
?????}
?????@Override
?????public?void?exec(ProductEvent?event)?{
?????????????//事件的源頭
?????????????Product?p?=?event.getSource();
?????????????//事件類型
?????????????ProductEventType?type?=?event.getEventType();
?????????????System.out.println("平民處理事件:"+p.getName()?+"誕生記,事件類型="+type);
?????}
}
平民只處理新建立的事件,其他事件不做處理,我們再來看貴族級別的事件處理者,如代碼清單36-12所示。
代碼清單36-12 貴族
public?class?Nobleman?extends?EventCustomer?{
?????//定義貴族能夠處理的事件的級別
?????public?Nobleman()?{
?????????????super(EventCustomType.EDIT);
?????????????super.addCustomType(EventCustomType.CLONE);
?????}
?????@Override
?????public?void?exec(ProductEvent?event)?{
?????????????//事件的源頭
?????????????Product?p?=?event.getSource();
?????????????//事件類型
?????????????ProductEventType?type?=?event.getEventType();
?????????????if(type.getValue()?==?EventCustomType.CLONE.getValue()){
??????????????????System.out.println("貴族處理事件:"+p.getName()?+"克隆,事件類型="+type);
?????????????}else{
??????????????????System.out.println("貴族處理事件:"+p.getName()?+"修改,事件類型="+type);
?????????????}
?????}
}
貴族稍有不同,它有兩個處理能力,能夠處理修改事件和克隆事件,同時在exec方法中對這兩類事件分別進行處理。此時,讀者可能會想到另外一個處理模式:責任鏈模式。建立一個鏈,然后兩類事件分別在鏈上進行處理并反饋結果。讀者可以參考一下Servlet的過濾器(Filter)的設計,在框架平臺的開發中可以采用該模式,它具有非常好的擴展性和穩定性。
所有的角色都已出場,我們建立一個場景類把它們串聯起來,如代碼清單36-13所示。
代碼清單36-13 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//獲得事件分發中心
?????????????EventDispatch?dispatch?=?EventDispatch.getEventDispatch();
?????????????//接受乞丐對事件的處理
?????????????dispatch.registerCustomer(new?Beggar());?????
?????????????//接受平民對事件的處理
?????????????dispatch.registerCustomer(new?Commoner());
?????????????//接受貴族對事件的處理
?????????????dispatch.registerCustomer(new?Nobleman());
?????????????//建立一個原子彈生產工廠
?????????????ProductManager?factory?=?new?ProductManager();
?????????????//制造一個產品
?????????????System.out.println("=====模擬創建產品事件========");
?????????????System.out.println("創建一個叫做小男孩的原子彈");
?????????????Product?p?=?factory.createProduct("小男孩原子彈");
?????????????//修改一個產品
?????????????System.out.println("\n=====模擬修改產品事件========");
?????????????System.out.println("把小男孩原子彈修改為胖子號原子彈");
?????????????factory.editProduct(p,?"胖子號原子彈");?????
?????????????//再克隆一個原子彈
?????????????System.out.println("\n=====模擬克隆產品事件========");
?????????????System.out.println("克隆胖子號原子彈");
?????????????factory.clone(p);
?????????????//遺棄一個產品
?????????????System.out.println("\n=====模擬銷毀產品事件========");
?????????????System.out.println("遺棄胖子號原子彈");
?????????????factory.abandonProduct(p);
?????}
}
運行結果如下所示:
=====模擬創建產品事件========
創建一個叫做小男孩的原子彈
平民處理事件:小男孩原子彈誕生記,事件類型=NEW_PRODUCT
=====模擬修改產品事件========
把小男孩原子彈修改為胖子號原子彈
貴族處理事件:胖子號原子彈修改,事件類型=EDIT_PRODUCT
=====模擬克隆產品事件========
克隆胖子號原子彈
貴族處理事件:胖子號原子彈克隆,事件類型=CLONE_PRODUCT
=====模擬銷毀產品事件========
遺棄胖子號原子彈
乞丐處理事件:胖子號原子彈銷毀,事件類型=DEL_PRODUCT
我們的事件處理框架已經生效了,有行為,就產生事件,并有處理事件的處理者,并且這三者都相互解耦,可以獨立地擴展下去。比如,想增加處理者,沒有問題,建立一個類繼承EventCustomer,然后注冊到EventDispatch上,就可以進行處理事件了;想擴展產品,沒問題?需要稍稍修改一下,首先抽取出產品和事件的抽象類,然后再進行擴展即可。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——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種設計模式彩圖