结构型模式--装饰模式
下面先用java,然后用Objective-C行對裝飾模式的講解:
對于java的裝飾模式講解和使用比較詳細和難度有點偏高,而對于Objective-C的裝飾模式講解和使用方面比較簡單,而且和java的裝飾模式略有差異,差異在于:java的裝飾模式是通過面向抽象來編程的,而Objective-C就沒有用到抽象或則協議這個應用,而是直接用對象引用被裝飾類來擴展對象的方法。我個人覺得這主要原因應該是java語言的實用場景往往是結構復雜的大項目,涉及的問題領域比較廣,而Objective-C應用領域僅僅是IOS前端app的開發,對設計模式結構上設計可能沒有java那么高的要求。
(Java)
盡管目前房價依舊很高,但還是阻止不了大家對新房的渴望和買房的熱情。如果大家買的是毛坯房,無疑還有一項艱巨的任務要面對,那就是裝修。對新房進 行裝修并沒有改變房屋用于居住的本質,但它可以讓房子變得更漂亮、更溫馨、更實用、更能滿足居家的需求。在軟件設計中,我們也有一種類似新房裝修的技術可 以對已有對象(新房)的功能進行擴展(裝修),以獲得更加符合用戶需求的對象,使得對象具有更加強大的功能。這種技術對應于一種被稱之為裝飾模式的設計模 式,本章將介紹用于擴展系統功能的裝飾模式。
圖形界面構件庫的設計
Sunny 軟件公司基于面向對象技術開發了一套圖形界面構件庫 VisualComponent,該構件庫提供了大量基本構件,如窗體、文本框、列表框等,由于在使用該構件庫時,用戶經常要求定制一些特效顯示效果,如 帶滾動條的窗體、帶黑色邊框的文本框、既帶滾動條又帶黑色邊框的列表框等等,因此經常需要對該構件庫進行擴展以增強其功能,如圖所示:
如何提高圖形界面構件庫性的可擴展性并降低其維護成本是 Sunny 公司開發人員必須面對的一個問題。
Sunny 軟件公司的開發人員針對上述要求,提出了一個基于繼承復用的初始設計方案,其基本結構如圖所示:
圖中,在抽象類 Component 中聲明了抽象方法 display(),其子類 Window、TextBox 等實現了 display() 方法,可以顯示最簡單的控件,再通過它們的子類來對功能進行擴展,例如,在 Window 的子類 ScrollBarWindow、BlackBorderWindow 中對 Window 中的 display() 方法進行擴展,分別實現帶滾動條和帶黑色邊框的窗體。仔細分析該設計方案,我們不難發現存在如下幾個問題:
(1)系統擴展麻煩,在某些編程語言中無法實現。如果用戶需要一個既帶滾動條又帶黑色邊框的窗體,在圖中通過增 加了一個新的類 ScrollBarAndBlackBorderWindow 來實現,該類既作為 ScrollBarWindow 的子類,又作為 BlackBorderWindow 的子類;但現在很多面向對象編程語言,如 Java、C# 等都不支持多重類繼承,因此在這些語言中無法通過繼承來實現對來自多個父類的方法的重用。此外,如果還需要擴展一項功能,例如增加一個透明窗體類 TransparentWindow,它是 Window 類的子類,可以將一個窗體設置為透明窗體,現在需要一個同時擁有三項功能(帶滾動條、帶黑色邊框、透明)的窗體,必須再增加一個類作為三個窗體類的子類, 這同樣在 Java 等語言中無法實現。系統在擴展時非常麻煩,有時候甚至無法實現。
(2)代碼重復。從圖中我們可以看出,不只是窗體需要設置滾動條,文本框、列表框等都需要設置滾動條,因此在 ScrollBarWindow、ScrollBarTextBox和ScrollBarListBox等類中都包含用于增加滾動條的方法 setScrollBar(),該方法的具體實現過程基本相同,代碼重復,不利于對系統進行修改和維護。
(3)系統龐大,類的數目非常多。如果增加新的控件或者新的擴展功能系統都需要增加大量的具體類,這將導致系統 變得非常龐大。在圖中,3 種基本控件和 2 種擴展方式需要定義 9 個具體類;如果再增加一個基本控件還需要增加 3 個具體類;增加一種擴展方式則需要增加更多的類,如果存在 3 種擴展方式,對于每一個控件而言,需要增加7個具體類,因為這 3 種擴展方式存在 7 種組合關系(大家自己分析為什么需要 7 個類?)。
總之,圖不是一個好的設計方案,怎么辦?如何讓系統中的類可以進行擴展但是又不會導致類數目的急劇增加?不用著急,讓我們先來分析為什么這個設計方案會存在如此多的問題。根本原因在于復用機制的不合理, 圖采用了繼承復用,例如在 ScrollBarWindow 中需要復用 Window 類中定義的 display() 方法,同時又增加新的方法 setScrollBar(),ScrollBarTextBox 和 ScrollBarListBox 都必須做類似的處理,在復用父類的方法后再增加新的方法來擴展功能。根據“合成復用原則”,在實現功能復用時,我們要多用關聯,少用繼承, 因此我們可以換個角度來考慮,將 setScrollBar() 方法抽取出來,封裝在一個獨立的類中,在這個類中定義一個 Component 類型的對象,通過調用 Component的 display() 方法來顯示最基本的構件,同時再通過 setScrollBar() 方法對基本構件的功能進行增強。由于 Window、ListBox 和 TextBox 都是 Component 的子類,根據“里氏代換原則”,程序在運行時,我們只要向這個獨立的類中注入具體的 Component 子類的對象即可實現功能的擴展。這個獨立的類一般稱為裝飾器(Decorator)或裝飾類,顧名思義,它的作用就是對原有對象進行裝飾,通過裝飾來擴展 原有對象的功能。
裝飾類的引入將大大簡化本系統的設計,它也是裝飾模式的核心,下面讓我們正式進入裝飾模式的學習。
?
裝飾模式概述
裝飾模式可以在不改變一個對象本身功能的基礎上給對象增加額外的新行為,在現實生活中,這種情況也到處存在,例如一張照片,我們可以不改變照片本 身,給它增加一個相框,使得它具有防潮的功能,而且用戶可以根據需要給它增加不同類型的相框,甚至可以在一個小相框的外面再套一個大相框。
裝飾模式是一種用于替代繼承的技術,它通過一種無須定義子類的方式來給對象動態增加職責,使用對象之間的關聯關系取代類之間的繼承關系。在裝飾模式中引入了裝飾類,在裝飾類中既可以調用待裝飾的原有類的方法,還可以增加新的方法,以擴充原有類的功能。
裝飾模式定義如下:
裝飾模式(Decorator Pattern):動態地給一個對象增加一些額外的職責,就增加對象功能來說,裝飾模式比生成子類實現更為靈活。裝飾模式是一種對象結構型模式。
在裝飾模式中,為了讓系統具有更好的靈活性和可擴展性,我們通常會定義一個抽象裝飾類,而將具體的裝飾類作為它的子類,裝飾模式結構如圖所示:
在裝飾模式結構圖中包含如下幾個角色:
-  
Component(抽象構件):它是具體構件和抽象裝飾類的共同父類,聲明了在具體構件中實現的業務方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象,實現客戶端的透明操作。
 -  
ConcreteComponent(具體構件):它是抽象構件類的子類,用于定義具體的構件對象,實現了在抽象構件中聲明的方法,裝飾器可以給它增加額外的職責(方法)。
 -  
Decorator(抽象裝飾類):它也是抽象構件類的子類,用于給具體構件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象構件對象的引用,通過該引用可以調用裝飾之前構件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。
 - ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向構件添加新的職責。每一個具體裝飾類都定義了一些新的行為,它可以調用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴充對象的行為。
 
由于具體構件類和裝飾類都實現了相同的抽象構件接口,因此裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任,換言之,客戶端并不會覺得對象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要創造更多子類的情況下,將對象的功能加以擴展。
裝飾模式的核心在于抽象裝飾類的設計,其典型代碼如下所示:
1 class Decorator implements Component 2 { 3 private Component component; //維持一個對抽象構件對象的引用 4 public Decorator(Component component) //注入一個抽象構件類型的對象 5 { 6 this.component=component; 7 } 8 9 public void operation() 10 { 11 component.operation(); //調用原有業務方法 12 } 13 }在抽象裝飾類 Decorator 中定義了一個 Component 類型的對象 component,維持一個對抽象構件對象的引用,并可以通過構造方法或 Setter 方法將一個 Component 類型的對象注入進來,同時由于 Decorator 類實現了抽象構件Component 接口,因此需要實現在其中聲明的業務方法 operation(),需要注意的是在 Decorator 中并未真正實現 operation() 方法,而只是調用原有 component 對象的 operation() 方法,它沒有真正實施裝飾,而是提供一個統一的接口,將具體裝飾過程交給子類完成。
在 Decorator 的子類即具體裝飾類中將繼承 operation() 方法并根據需要進行擴展,典型的具體裝飾類代碼如下:
1 class ConcreteDecorator extends Decorator 2 { 3 public ConcreteDecorator(Component component) 4 { 5 super(component); 6 } 7 8 public void operation() 9 { 10 super.operation(); //調用原有業務方法 11 addedBehavior(); //調用新增業務方法 12 } 13 14 //新增業務方法 15 public void addedBehavior() 16 { 17 …… 18 } 19 }在具體裝飾類中可以調用到抽象裝飾類的 operation() 方法,同時可以定義新的業務方法,如 addedBehavior()。
由于在抽象裝飾類 Decorator 中注入的是 Component 類型的對象,因此我們可以將一個具體構件對象注入其中,再通過具體裝飾類來進行裝飾;此外,我們還可以將一個已經裝飾過的 Decorator 子類的對象再注入其中進行多次裝飾,從而對原有功能的多次擴展。
思考
能否在裝飾模式中找出兩個獨立變化的維度?試比較裝飾模式和橋接模式的相同之處和不同之處?
(這個問題我還沒有去思考,因為我還沒學習橋接模式)
完整解決方案
為了讓系統具有更好的靈活性和可擴展性,克服繼承復用所帶來的問題,Sunny 公司開發人員使用裝飾模式來重構圖形界面構件庫的設計,其中部分類的基本結構如圖所示:
在圖中,Component 充當抽象構件類,其子類 Window、TextBox、ListBox 充當具體構件類,Component 類的另一個子類 ComponentDecorator 充當抽象裝飾類,ComponentDecorator 的子類 ScrollBarDecorator 和 BlackBorderDecorator 充當具體裝飾類。完整代碼如下所示:
1 //抽象界面構件類:抽象構件類,為了突出與模式相關的核心代碼,對原有控件代碼進行了大量的簡化 2 abstract class Component 3 { 4 public abstract void display(); 5 } 6 7 //窗體類:具體構件類 8 class Window extends Component 9 { 10 public void display() 11 { 12 System.out.println("顯示窗體!"); 13 } 14 } 15 16 //文本框類:具體構件類 17 class TextBox extends Component 18 { 19 public void display() 20 { 21 System.out.println("顯示文本框!"); 22 } 23 } 24 25 //列表框類:具體構件類 26 class ListBox extends Component 27 { 28 public void display() 29 { 30 System.out.println("顯示列表框!"); 31 } 32 } 33 34 //構件裝飾類:抽象裝飾類 35 class ComponentDecorator extends Component 36 { 37 private Component component; //維持對抽象構件類型對象的引用 38 39 public ComponentDecorator(Component component) //注入抽象構件類型的對象 40 { 41 this.component = component; 42 } 43 44 public void display() 45 { 46 component.display(); 47 } 48 } 49 50 //滾動條裝飾類:具體裝飾類 51 class ScrollBarDecorator extends ComponentDecorator 52 { 53 public ScrollBarDecorator(Component component) 54 { 55 super(component); 56 } 57 58 public void display() 59 { 60 this.setScrollBar(); 61 super.display(); 62 } 63 64 public void setScrollBar() 65 { 66 System.out.println("為構件增加滾動條!"); 67 } 68 } 69 70 //黑色邊框裝飾類:具體裝飾類 71 class BlackBorderDecorator extends ComponentDecorator 72 { 73 public BlackBorderDecorator(Component component) 74 { 75 super(component); 76 } 77 78 public void display() 79 { 80 this.setBlackBorder(); 81 super.display(); 82 } 83 84 public void setBlackBorder() 85 { 86 System.out.println("為構件增加黑色邊框!"); 87 } 88 }編寫如下客戶端測試代碼:
1 class Client 2 { 3 public static void main(String args[]) 4 { 5 Component component,componentSB; //使用抽象構件定義 6 component = new Window(); //定義具體構件 7 componentSB = new ScrollBarDecorator(component); //定義裝飾后的構件 8 componentSB.display(); 9 } 10 }編譯并運行程序,輸出結果如下:
為構件增加滾動條! 顯示窗體!在客戶端代碼中,我們先定義了一個 Window 類型的具體構件對象 component,然后將 component 作為構造函數的參數注入到具體裝飾類 ScrollBarDecorator 中,得到一個裝飾之后對象 componentSB,再調用 componentSB 的 display() 方法后將得到一個有滾動條的窗體。如果我們希望得到一個既有滾動條又有黑色邊框的窗體,不需要對原有類庫進行任何修改,只需將客戶端代碼修改為如下所示:
1 class Client 2 { 3 public static void main(String args[]) 4 { 5 Component component,componentSB,componentBB; //全部使用抽象構件定義 6 component = new Window(); 7 componentSB = new ScrollBarDecorator(component); 8 componentBB = new BlackBorderDecorator(componentSB); //將裝飾了一次之后的對象繼續注入到另一個裝飾類中,進行第二次裝飾 9 componentBB.display(); 10 } 11 }編譯并運行程序,輸出結果如下:
為構件增加黑色邊框! 為構件增加滾動條! 顯示窗體!我們可以將裝飾了一次之后的 componentSB 對象注入另一個裝飾類 BlackBorderDecorator 中實現第二次裝飾,得到一個經過兩次裝飾的對象 componentBB,再調用 componentBB 的 display() 方法即可得到一個既有滾動條又有黑色邊框的窗體。
如果需要在原有系統中增加一個新的具體構件類或者新的具體裝飾類,無須修改現有類庫代碼,只需將它們分別作為抽象構件類或者抽象裝飾類的子類即可。 與圖所示的繼承結構相比,使用裝飾模式之后將大大減少了子類的個數,讓系統擴展起來更加方便,而且更容易維護,是取代繼承復用的有效方式之一。
?
透明裝飾模式與半透明裝飾模式
裝飾模式雖好,但存在一個問題。如果客戶端希望單獨調用具體裝飾類新增的方法,而不想通過抽象構件中聲明的方法來調用新增方法時將遇到一些麻煩,我們通過一個實例來對這種情況加以說明:
在 Sunny 軟件公司開發的 Sunny OA 系統中,采購單(PurchaseRequest)和請假條(LeaveRequest)等文件(Document)對象都具有顯示功能,現在要為其增加審批、刪除等功能,使用裝飾模式進行設計。
我們使用裝飾模式可以得到如圖所示結構圖:
在圖中,Document充當抽象構件類,PurchaseRequest 和 LeaveRequest 充當具體構件類,Decorator 充當抽象裝飾類,Approver 和 Deleter 充當具體裝飾類。其中 Decorator 類和 Approver 類的示例代碼如下所示:
1 //抽象裝飾類 2 class Decorator implements Document 3 { 4 private Document document; 5 6 public Decorator(Document document) 7 { 8 this. document = document; 9 } 10 11 public void display() 12 { 13 document.display(); 14 } 15 } 16 17 //具體裝飾類 18 class Approver extends Decorator 19 { 20 public Approver(Document document) 21 { 22 super(document); 23 System.out.println("增加審批功能!"); 24 } 25 26 public void approve() 27 { 28 System.out.println("審批文件!"); 29 } 30 }大家注意,Approver 類繼承了抽象裝飾類 Decorator 的 display() 方法,同時新增了業務方法 approve(),但這兩個方法是獨立的,沒有任何調用關系。如果客戶端需要分別調用這兩個方法,代碼片段如下所示:
1 Document doc; //使用抽象構件類型定義 2 doc = new PurchaseRequest(); 3 Approver newDoc; //使用具體裝飾類型定義 4 newDoc = new Approver(doc); 5 newDoc.display();//調用原有業務方法 6 newDoc.approve();//調用新增業務方法如果newDoc也使用Document類型來定義,將導致客戶端無法調用新增業務方法approve(),因為在抽象構件類Document中沒有對approve()方法的聲明。也就是說,在客戶端無法統一對待裝飾之前的具體構件對象和裝飾之后的構件對象。
在實際使用過程中,由于新增行為可能需要單獨調用,因此這種形式的裝飾模式也經常出現,這種裝飾模式被稱為半透明(Semi- transparent)裝飾模式,而標準的裝飾模式是透明(Transparent)裝飾模式。下面我們對這兩種裝飾模式進行較為詳細的介紹:
透明裝飾模式
在透明裝飾模式中,要求客戶端完全針對抽象編程,裝飾模式的透明性要求客戶端程序不應該將對象聲明為具體構件類型或具體裝飾類型,而應該全部聲明為抽象構件類型。對于客戶端而言,具體構件對象和具體裝飾對象沒有任何區別。也就是應該使用如下代碼:
1 Component c, c1; //使用抽象構件類型定義對象 2 c = new ConcreteComponent(); 3 c1 = new ConcreteDecorator (c); 而不是使用如下代碼: 1 ConcreteComponent c; //使用具體構件類型定義對象 2 c = new ConcreteComponent();或
1 ConcreteDecorator c1; //使用具體裝飾類型定義對象 2 c1 = new ConcreteDecorator(c);在12.3節圖形界面構件庫的設計方案中使用的就是透明裝飾模式,在客戶端中存在如下代碼片段:
1 …… 2 Component component,componentSB,componentBB; //全部使用抽象構件定義 3 component = new Window(); 4 componentSB = new ScrollBarDecorator(component); 5 componentBB = new BlackBorderDecorator(componentSB); 6 componentBB.display(); 7 ……使用抽象構件類型 Component 定義全部具體構件對象和具體裝飾對象,客戶端可以一致地使用這些對象,因此符合透明裝飾模式的要求。
透明裝飾模式可以讓客戶端透明地使用裝飾之前的對象和裝飾之后的對象,無須關心它們的區別,此外,還可以對一個已裝飾過的對象進行多次裝飾,得到更 為復雜、功能更為強大的對象。在實現透明裝飾模式時,要求具體裝飾類的 operation() 方法覆蓋抽象裝飾類的 operation() 方法,除了調用原有對象的 operation() 外還需要調用新增的 addedBehavior() 方法來增加新行為,
半透明裝飾模式
透明裝飾模式的設計難度較大,而且有時我們需要單獨調用新增的業務方法。為了能夠調用到新增方法,我們不得不用具體裝飾類型來定義裝飾之后的對象, 而具體構件類型還是可以使用抽象構件類型來定義,這種裝飾模式即為半透明裝飾模式,也就是說,對于客戶端而言,具體構件類型無須關心,是透明的;但是具體 裝飾類型必須指定,這是不透明的。如本節前面所提到的文件對象功能增加實例,為了能夠調用到在 Approver 中新增方法 approve(),客戶端代碼片段如下所示:
1 …… 2 Document doc; //使用抽象構件類型定義 3 doc = new PurchaseRequest(); 4 Approver newDoc; //使用具體裝飾類型定義 5 newDoc = new Approver(doc); 6 ……半透明裝飾模式可以給系統帶來更多的靈活性,設計相對簡單,使用起來也非常方便;但是其最大的缺點在于不能實現對同一個對象的多次裝飾,而且客戶端 需要有區別地對待裝飾之前的對象和裝飾之后的對象。在實現半透明的裝飾模式時,我們只需在具體裝飾類中增加一個獨立的 addedBehavior() 方法來封裝相應的業務處理,由于客戶端使用具體裝飾類型來定義裝飾后的對象,因此可以單獨調用 addedBehavior() 方法來擴展系統功能。
思考
為什么半透明裝飾模式不能實現對同一個對象的多次裝飾?
裝飾模式注意事項
在使用裝飾模式時,通常我們需要注意以下幾個問題:
(1) 盡量保持裝飾類的接口與被裝飾類的接口相同,這樣,對于客戶端而言,無論是裝飾之前的對象還是裝飾之后的對象都可以一致對待。這也就是說,在可能的情況下,我們應該盡量使用透明裝飾模式。
(2) 盡量保持具體構件類 ConcreteComponent 是一個“輕”類,也就是說不要把太多的行為放在具體構件類中,我們可以通過裝飾類對其進行擴展。
(3) 如果只有一個具體構件類,那么抽象裝飾類可以作為該具體構件類的直接子類。如圖所示:
裝飾模式總結
裝飾模式降低了系統的耦合度,可以動態增加或刪除對象的職責,并使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,以便增加新的具體構件類和具體 裝飾類。在軟件開發中,裝飾模式應用較為廣泛,例如在 JavaIO 中的輸入流和輸出流的設計、javax.swing 包中一些圖形界面構件功能的增強等地方都運用了裝飾模式。
主要優點
裝飾模式的主要優點如下:
(1) 對于擴展一個對象的功能,裝飾模式比繼承更加靈活性,不會導致類的個數急劇增加。
(2) 可以通過一種動態的方式來擴展一個對象的功能,通過配置文件可以在運行時選擇不同的具體裝飾類,從而實現不同的行為。
(3) 可以對一個對象進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合,得到功能更為強大的對象。
(4) 具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,原有類庫代碼無須改變,符合“開閉原則”。
主要缺點
裝飾模式的主要缺點如下:
(1) 使用裝飾模式進行系統設計時將產生很多小對象,這些對象的區別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對象的產生勢必會占用更多的系統資源,在一定程序上影響程序的性能。
(2) 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易于出錯,排錯也很困難,對于多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為繁瑣。
適用場景
在以下情況下可以考慮使用裝飾模式:
(1) 在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。
(2) 當不能采用繼承的方式對系統進行擴展或者采用繼承不利于系統擴展和維護時可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統中存在大量獨立的 擴展,為支持每一種擴展或者擴展之間的組合將產生大量的子類,使得子類數目呈爆炸性增長;第二類是因為類已定義為不能被繼承(如 Java 語言中的 final 類)。
練習
Sunny 軟件公司欲開發了一個數據加密模塊,可以對字符串進行加密。最簡單的加密算法通過對字母進行移位來實現,同時還提供了稍復雜的逆向輸出加密,還提供了更為 高級的求模加密。用戶先使用最簡單的加密算法對字符串進行加密,如果覺得還不夠可以對加密之后的結果使用其他加密算法進行二次加密,當然也可以進行第三次 加密。試使用裝飾模式設計該多重加密系統。
(本練習還可以用 策略模式 進行設計)
(Objective-C)
?下面的GamePad類是原始類(原來開始的類,我個人的說法,請不要見怪,嘻嘻),對于GamePad類我們只需要知道它內部提供的所有的方法,但是可以不用知道方法細節怎么實現,我們先創建一個裝飾類GamePadDecorator類,在這個裝飾類中:如下圖,我們先創建一個原始類的實例對象,實例化對象放置在裝飾類的初始化方法中,然后寫出所有和原始類內部方法同名的方法作為裝飾原始類方法,并把調用原始類方法放置在這些同名的裝飾類方法中。這樣就實現了一個裝飾類。
?
?
?
?
?
?
?
?
?
?
?
然后拓展這個裝飾類,我們就可以通過繼承這個類,然后在這個繼承類中添加需要拓展的方法(如下圖中的cheat方法就是新拓展的方法,請看cheat方法的具體實現): 以上就是Objective-C通過裝飾模式拓展一個類的方法,下面還需要不得不提到的就是Coco框架提供了一種拓展類方法的方式就是:Category的使用。Category是Coco框架提供的裝飾模式的一種方式,但是它和真正的裝飾模式卻有著細微的區別,這些細微的區別體現在如何給Category增加屬性的時候以及Category重寫被裝飾對象方法的一些區別。這個區別卻也是一個鮮為人知的細節。那就是如果在Category的類中你要是重寫被裝飾的對象的方法,即使你在使用原始類的過程中,沒有引用這個Category類,你使用那個原始類中的被重寫的方法,實現的結果確實Category里重寫的方法所實現的結果。這個細節很可能會成為你一不小心犯下的影響整個項目的錯誤。
而相比較前面的裝飾模式的使用來擴展類的方法,其中需要擴展的時候,是通過繼承裝飾類來擴展的,這樣,你在子類中重寫了父類的方法,然而你不引用(import)子類,使用父類的那個被重寫的方法,實現的結果也不會出現子類的重寫方法實現的結果。 總之,站在IOS開發的角度來總結這兩種情況: 1、當不需要重寫父類方法,然后卻需要拓展父類方法的時候,請使用Category。 2、當需要重寫父類的方法,然后又需要拓展父類方法的時候,請使用正兒八經的裝飾模式。 學習來自《極客學院》 最后,關于裝飾者模式,還需要繼續提高的還可以觀看《極客學院》的java的《設計模式之裝飾者模式》 >> 留言時間:2017年2月10日 今天嘗試在“柚子網”IOS項目開發中使用裝飾模式來解耦一個代碼耦合度比較高的模塊,這個模塊是“企業端發布和編輯職位”,該模塊已經完成,代碼行數是1700多行,很多控件,很多邏輯都寫在此模塊中。該模塊載體是MVC模式中的Controller控制器,而IOS的控制器Controller中依賴于Cocoa框架的UIViewController,在使用裝飾模式的過程中,收到了限制。?? ?思路:打算將該模塊的Controller,采用策略模式進行解耦成兩個小模塊:“發布職位”和“編輯職位”。裝飾模式相對于直接繼承的好處是,能夠將需要添加的行為或者對象作為裝飾品將原來固有的基礎行為或者對象進行添加和包裝。當不需要這個裝飾的時候,直接不包裝即可。而且不同的裝飾模式可以獨立耦合開來。但是,在應用在Controller對象中,卻受到Cocoa模塊提供的UIViewController的一些已有的屬性的限制,比如view,這里新的VIewController裝飾對象需要擁有舊的ViewController被裝飾對象的基礎屬性,因為其生命周期的執行并不是代碼手動調用的,而是Cocoa框架自動調用的。還有原有的ViewController中的可點擊控件監聽事件依賴self(當前控制器對象),而裝飾模式的設計就是裝飾對象和被裝飾對象是相互獨立的對象,這就導致很多被裝飾對象中的點擊監聽事件到了裝飾對象中就無效了,只能在裝飾對象中,重寫監聽事件的方法,這樣還不如直接繼承模式來的更方便。
?? ?綜上所述:
?? ??? ?1、在Cocoa提供的ViewController對象中,本身很多生命周期只能繼承中使用,所以這里不適合對Controller進行裝飾模式的使用。
?? ??? ?2、裝飾模式是結構模式,ViewController的生命周期的邏輯是Cocoa框架固有不可改變的結構。所以在這里不適合采用裝飾模式,所以,裝飾模式就比較試用于自定義設計的模塊并且和其他模塊耦合度不高的模塊。
?? ??? ?3、設計具有場景實用性,即使有的場景邏輯用裝飾模式可以達到理想的好處,但是并不是所有的場景都能試用,要從實際出發。 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的结构型模式--装饰模式的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: ios学习笔记block回调的应用(一个
 - 下一篇: 软件产品需求规格说明书模板