裝飾者模式 (Decorator)java
動態地給一個對象添加一些額外的職責。是一種結構式模式,就增長功能來講,Decorator 模式相比生成子類更爲靈活。換言之,客戶端並不會以爲對象在裝飾前和裝飾後有什麼不一樣。裝飾模式能夠在不使用創造更多子類的狀況下,將對象的功能加以擴展。設計模式
要點: 裝飾者與被裝飾者擁有共同的超類,繼承的目的是繼承類型,而不是行爲.ide
裝飾模式的類圖以下:性能
在裝飾模式中的角色有:測試
● 抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。this
● 具體構件(ConcreteComponent)角色:定義一個將要接收附加責任的類。設計
● 裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。code
● 具體裝飾(ConcreteDecorator)角色:負責給構件對象「貼上」附加的責任。component
代碼說明:對象
抽象構件角色:
public interface Component{ public void sampleOperation(); }
具體掛件角色:
public class ConcreteComponent implements Component{ @Override public void sampleOperation() { System.out.println("初始行爲"); } }
裝飾角色:
public class Decorator implements Component{ private Component component; public Decorator(Component component){ this.component = component; } @Override public void sampleOperation() { // 委派給 構件 component.sampleOperation(); } }
具體裝飾角色
public class ConcreteDecoratorA extends Decorator{ public ConcreteDecoratorA(Component component) { super(component); } @Override public void sampleOperation() { super.sampleOperation(); // 其餘 可擴展的行爲 } }
實例說明(齊天大聖的例子):
孫悟空有七十二般變化,他的每一種變化都給他帶來一種附加的本領。他變成魚兒時,就能夠到水裏游泳;他變成鳥兒時,就能夠在天上飛行。
本例中,Component 的角色便由鼎鼎大名的齊天大聖扮演;ConcreteComponent 的角色屬於大聖的本尊,就是猢猻本人;Decorator 的角色由大聖的七十二變扮演。而 ConcreteDecorator 的角色即是魚兒、鳥兒等七十二般變化。
抽象構件角色: 大聖的稱號
public interface TheGreatestSage { public void move(); }
具體構件角色 :「大聖本尊」猢猻類
public class Monkey implements TheGreatestSage{ @Override public void move() { System.out.println("Monkey Move"); } }
抽象 裝飾者 角色: 「七十二變」
public class Change implements TheGreatestSage{ private TheGreatestSage sage; public Change(TheGreatestSage sage){ this.sage = sage; } @Override public void move() { sage.move(); } }
具體裝飾角色 :「魚兒」
public class Fish extends Change{ public Fish(TheGreatestSage sage) { super(sage); } @Override public void move() { System.out.println("Fish Move"); System.out.println("Fish 的 其餘行爲動做描述"); } }
具體裝飾角色 :「小鳥」
public class Bird extends Change{ public Bird(TheGreatestSage sage) { super(sage); } @Override public void move() { System.out.println("Bird Move"); System.out.println("Bird 的 其餘行爲動做描述"); } }
測試:
public class Client { public static void main(String[] args) { TheGreatestSage sage = new Monkey(); Fish fish = new Fish(sage); fish.move(); Bird bird = new Bird(fish); bird.move(); } }
上面的例子中,系統把大聖從一隻猢猻裝飾成了一隻鳥兒(把鳥兒的功能加到了猢猻身上),而後又把鳥兒裝飾成了一條魚兒(把魚兒的功能加到了猢猻+鳥兒身上,獲得了猢猻+鳥兒+魚兒)。
裝飾模式的簡化
大多數狀況下,裝飾模式的實現都要比上面給出的示意性例子要簡單。
若是隻有一個ConcreteComponent類,那麼能夠考慮去掉抽象的Component類(接口),把Decorator做爲一個ConcreteComponent子類。以下圖所示:
若是隻有一個ConcreteDecorator類,那麼就沒有必要創建一個單獨的Decorator類,而能夠把Decorator和ConcreteDecorator的責任合併成一個類。甚至在只有兩個ConcreteDecorator類的狀況下,均可以這樣作。以下圖所示:
半透明的 裝飾模式
然而,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,加強所考慮的類的性能。在加強性能的時候,每每須要創建新的公開的方法。即使是在孫大聖的系統裏,也須要新的方法。好比齊天大聖類並無飛行的能力,而鳥兒有。這就意味着鳥兒應當有一個新的fly()方法。再好比,齊天大聖類並無游泳的能力,而魚兒有,這就意味着在魚兒類裏應當有一個新的swim()方法。
這就致使了大多數的裝飾模式的實現都是「半透明」的,而不是徹底透明的。換言之,容許裝飾模式改變接口,增長新的方法。這意味着客戶端能夠聲明ConcreteDecorator類型的變量,從而能夠調用ConcreteDecorator類中才有的方法:
TheGreatestSage sage = new Monkey(); Bird bird = new Bird(sage); bird.fly();
半透明的裝飾模式是介於裝飾模式和適配器模式之間的。適配器模式的用意是改變所考慮的類的接口,也能夠經過改寫一個或幾個方法,或增長新的方法來加強或改變所考慮的類的功能。大多數的裝飾模式其實是半透明的裝飾模式,這樣的裝飾模式也稱作半裝飾、半適配器模式。
裝飾模式的優勢:
(1)裝飾模式與繼承關係的目的都是要擴展對象的功能,可是裝飾模式能夠提供比繼承更多的靈活性。裝飾模式容許系統動態決定「貼上」一個須要的「裝飾」,或者除掉一個不須要的「裝飾」。繼承關係則不一樣,繼承關係是靜態的,它在系統運行前就決定了。
(2)經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,設計師能夠創造出不少不一樣行爲的組合。
裝飾模式的缺點:
因爲使用裝飾模式,能夠比使用繼承關係須要較少數目的類。使用較少的類,固然使設計比較易於進行。可是,在另外一方面,使用裝飾模式會產生比使用繼承關係更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。
-------------------------------------------------------------------------------------------------------------------------------------------------------
設計模式在JAVA I/O庫中的應用
裝飾模式在Java語言中的最著名的應用莫過於Java I/O標準庫的設計了。
Java I/O庫的對象結構圖以下,因爲Java I/O的對象衆多,所以只畫出InputStream的部分。
根據上圖能夠看出:
● 抽象構件(Component)角色:由InputStream扮演。這是一個抽象類,爲各類子類型提供統一的接口。
● 具體構件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等類扮演。它們實現了抽象構件角色所規定的接口。
● 抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實現了InputStream所規定的接口。
● 具體裝飾(ConcreteDecorator)角色:由幾個類扮演,分別是BufferedInputStream、DataInputStream以及兩個不經常使用到的類LineNumberInputStream、PushbackInputStream。
例如:
public class IOTest { public static void main(String[] args) throws IOException { // 流式讀取文件 DataInputStream dis = null; try{ dis = new DataInputStream( new BufferedInputStream( new FileInputStream("test.txt") ) ); //讀取文件內容 byte[] bs = new byte[dis.available()]; dis.read(bs); String content = new String(bs); System.out.println(content); }finally{ dis.close(); } } }
觀察上面的代碼,會發現最裏層是一個FileInputStream對象,而後把它傳遞給一個BufferedInputStream對象,通過BufferedInputStream處理,再把處理後的對象傳遞給了DataInputStream對象進行處理,這個過程其實就是裝飾器的組裝過程,FileInputStream對象至關於原始的被裝飾的對象,而BufferedInputStream對象和DataInputStream對象則至關於裝飾器。