什麼是設計模式:在我們進行程序設計時,逐漸形成了一些典型問題和問題的解決方案,這就是軟件模式;每一個模式描述了一個在我們程序設計中經常發生的問題,以及該問題的解決方案;當我們碰到模式所描述的問題,就可以直接用相應的解決方法去解決這個問題,這就是設計模式。
設計模式就是抽象出來的東西,它不是學出來的,是用出來的;或許你根本不知道任何模式,不考慮任何模式,卻寫著最優秀的代碼,即使以“模式專家”的角度來看,都是最佳的設計,不得不說是“最佳的模式實踐”,這是因為你積累了很多的實踐經驗,知道“在什麼場合代碼應該怎麼寫”,這本身就是設計模式。
有人說:“水平沒到,學也白學,水平到了,無師自通”。誠然,模式背熟,依然可能寫不出好代碼,更別說設計出好框架;OOP理解及實踐經驗到達一定水平,同時也意味著總結了很多好的設計經驗,但"無師自通",卻也未必盡然,或者可以說,恰恰是在水平和經驗的基礎上,到了該系統的學習一下“模式”的時候了,學習一下專家總結的結果,印證一下自己的不足,對於提高水平還是很有幫助的。
本系列的設計模式學習筆記,實際是對於《Java與模式》這本書的學習記錄。
裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方案。
裝飾模式使用原來被裝飾的類的一個子類的實例,把客戶端的調用委派到被裝飾類。裝飾模式的關鍵在於這種擴展是完全透明的。
(1)抽象構件(Component)角色:給出一個抽象接口,以規范准備接收附加責任的對象。
(2)具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
(3)裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。雖然Decorator類不是一個抽象類,在實際應用中也不一定是抽象類,但是由於它的功能是一個抽象角色,因此也常常稱它為抽象裝飾。
(4)集體裝飾(Concrete Decorator)角色:負責給構件對象“貼上”附加的責任。
interface Component { //商業方法 void sampleOp(); } class ConcreteComponent implements Component { public ConcreteComponent() { //Write your code } //商業方法 public void sampleOp() { //Write your code } } class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } public Decorator() { //Write your code } //商業方法 public void sampleOp() { component.sampleOp(); } } class ConcreteDecorator extends Decorator { //商業方法 public void sampleOp() { super.sampleOp(); } }
(1)需要擴展一個類的功能,或給一個類增加附加責任。
(2)需要動態地給一個對象增加功能,這些功能可以再動態的撤銷。
(3)需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使基礎關系變得不現實。
個人感覺,除了JDK中IO類的設計,工作場景中幾乎碰不到裝飾模式的使用,完全不像適配器模式,隨處可見。
(1)裝飾模式與繼承的目的都是要擴展對象的功能,但是裝飾模式可以提供比繼承更多的靈活性。裝飾模式允許系統動態地決定“貼上”一個需要的“裝飾”,或者除掉一個不需要的“裝飾”。繼承關系則不同,繼承關系是靜態的,它在系統運行前就決定了。
(2)通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出更多不同行為的組合。而繼承關系則沒有這個優勢,每一種不同的排列組合均需要事先通過子類的繼承方式設計好。
(3)這種比繼承更加靈活機動的特性,也同時意味著裝飾模式比繼承更加易於出錯。
(4)使用裝飾模式,可以比使用繼承關系需要較少數目的類。使用較少的類,當然使設計比較易於進行。但是,在另一方面,使用裝飾模式會產生比使用繼承關系更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。
大多數情況下,裝飾模式的實現都比本節的定義中給出的示意性實現要簡單。對模式進行簡化時,需要注意以下的情況:
(1)一個裝飾類的接口必須與被裝飾類的接口相容。ConcreteDecorator類和ConcreteComponent必須繼承自一個共同的父類,這個在裝飾模式結構圖中就有說明,但是在實際使用時,如果在模式的實現上有所簡化,就必須特別注意這一點。
(2)盡量保持Component作為一個“輕”類。抽象構件的職責是接口而不是存儲數據嗎,注意不要把太多的邏輯和狀態放在Component類裡。Component可以是接口、抽象類或者具體類。
(3)如果只有一個ComcreteComponent類而沒有抽象的Component類(接口),那麼Decorator類經常可以是ConcreteComponent的一個子類,如下圖:
由上圖可知,沒有抽象的接口Component也是可以的,但ConcreteComponent就要扮演雙重的角色。
(4)如果只有一個ConcreteDecorator類,那麼就沒有必要建立一個單獨的Decorator類,而可以吧Decorator和ConcreteDecorator的責任合並成一個類。甚至只有兩個ConcreteDecorator類的情況下,也可以這樣做;但是具體裝飾類大於三個的話,使用一個單獨的抽象裝飾類就有必要了,如下圖:
編程。裝飾模式對客戶端的透明性要求程序不要聲明一個ConcreteDecorator類型的變量,而應當聲明一個Component類型的變量。
(2)然而,純碎的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,增強所考慮的類的性能。在增強性能的時候,往往需要建立新的公開的方法。這就導致了大多數的裝飾模式的實現都是“半透明”(semi-transparent)的,而不是完全“透明”的。換言之,允許裝飾模式改變接口,增加新的方法。這意味著客戶端可以聲明ConcreteDecorator類型的變量,從而可以調用ConcreteDecorator類才有的方法。
(3)半透明的裝飾模式介於裝飾模式和適配器模式之間的。適配器模式的用意是改變所考慮的類的接口,也可以通過改寫一個或幾個方法,或增加新的方法來增強或改變所考慮的類的功能。大多數的裝飾模式實際上是半透明的裝飾模式,這樣的裝飾模式也稱做“半裝飾、半適配器模式”。
(4)在模式研究圈子裡,所謂半透明的裝飾模式又叫做退化的(degenerate)裝飾模式。