裝飾者模式java
裝飾者模式是23種設計模式之一,是指在不改變原來的類和使用繼承的方式,動態的擴展這個類的功能。裝飾者容許向一個現有的對象添加特定的功能卻不改變它的結構。經過一個類來包裝原有的類來提供額外的功能。設計模式
裝飾者模式的結構:ide
Component:抽象組件,被裝飾的原始對象,能夠是抽象類或者接口學習
ConcreatCompoent:實現或者繼承了抽象組件,被裝飾的具體的實現this
Decorator:抽象的裝飾者,實現或者繼承了Component抽象組件,持有抽象組件的引用spa
ConcreatDecotarorA、ConcreatDecotarorB:具體的裝飾者,實現或者繼承了抽象裝飾者。設計
實現3d
舉個簡單的例子,假設有一個科沃斯機器人,它的基本功能是掃地,如今要求這個機器人能夠邊掃地邊唱歌,還能夠邊掃地邊跳舞。code
那麼,就能夠將機器定義爲一個抽象的類,裏面有一個抽象的方法,sweep();對象
/** * 抽象組件 */ public abstract class Robot { /** * 打掃的方法 */ public abstract void sweep(); }
被裝飾的具體對象:
/** * 抽象組件的具體實現類,即被裝飾者對象 */ public class ConcreatRobot extends Robot { @Override public void sweep() { System.out.println("打掃臥室"); } }
抽象裝飾者:
/** * 抽象的裝飾者類 */ public class DecoratorRobot extends Robot { private Robot robot; public DecoratorRobot(Robot robot) { this.robot = robot; } @Override public void sweep() { robot.sweep(); } }
具體的裝飾者A:
/** * 裝飾者A */ public class ConcreatDecoratorA extends DecoratorRobot { public ConcreatDecoratorA(Robot robot) { super(robot); } /** * 一遍掃地一邊唱歌 */ @Override public void sweep() { super.sweep(); this.sign(); } /** * 唱歌 */ public void sign(){ System.out.println("唱一首單身情歌"); } }
具體的裝飾者B:
/** * 裝飾者B */ public class ConcreatDecoratorB extends RobotDecorator { public ConcreatDecoratorB(Robot robot) { super(robot); } /** * 一遍掃地一邊跳舞 */ @Override public void sweep() { super.sweep(); dance(); } /** * 唱歌 */ public void dance(){ System.out.println("跳一支探戈"); } }
客戶端調用:
public class DecoratorTest { public static void main(String[] args) { Robot robot = new ConcreatRobot(); RobotDecorator signRobot = new ConcreatDecoratorA(robot); signRobot.sweep(); RobotDecorator danceRobot = new ConcreatDecoratorB(robot); danceRobot.sweep(); } } 控制檯輸出: 打掃臥室 唱一首單身情歌 打掃臥室 跳一支探戈
從例子中能夠看出,裝飾的模式是一種組合的概念,所謂裝飾,就是要去擴展被裝飾者從而擴展功能,而咱們客戶端在調用的時候,能夠選擇合適的裝飾類來達到咱們想要的結果。好比我只想讓機器人掃地,那麼我只須要調用具體的抽象實現類,我想讓機器人又掃地又跳舞,那麼就使用包裝類。
裝飾者模式是爲已有功能動態的添加更多功能的一種方式。那麼,在何時使用呢,當系統須要新的功能的時候,向舊代碼中添加新的代碼顯然增長了原有代碼的複雜度,而且違反了開閉原則。這個時候,裝飾者提供了一個很是好的機會,咱們能夠經過包裝這個主類,把每一個要包裝的功能單獨的寫成一個包裝類,從而擴展該類的功能。客戶端能夠根據須要,調用特定功能的包裝類。這樣作的好處是把類中的裝飾功能或者和核心功能分開,簡化原有的類。
裝飾者模式在java IO中的應用
裝飾者模式在JDK中的典型應用莫過於IO體系了。Java中的IO分爲兩種,字節流和字符流,其中每一種分別能夠分爲輸入流和輸出流,咱們來看下IO體系的結構圖。
咱們以InputStream爲例,InputStream是抽象的父類,FilterInputtream和ByteArrayInputStream繼承了InputStream,這些類繼承了InputStream的基本的字節讀取功能,FilterInputtream還持有了InputStream的引用,它的全部的方法,都是調用了這個引用的同名方法,也就是說,他把全部的調用都委託給了InputStream。而且FilterInputtream還有一些子類,好比BufferdInputStream,DataInputStream等。BufferedInputStream提供了緩衝輸入流,當咱們經過read()讀取輸入流的數據時,BufferedInputStream會將該輸入流的數據分批的填入到緩衝區中。每當緩衝區中的數據被讀完以後,輸入流會再次填充數據緩衝區;如此反覆,直到咱們讀完輸入流數據位置。因而可知,BufferedInputStream爲咱們提供了一個從緩衝區讀取數據的功能,就像是科沃斯機器人,通過包裝後爲咱們提供了唱歌的功能同樣。咱們來看下這幾個類的類圖。
正如圖中所示,他們的結構徹底知足裝飾者模式的結構圖。同理,像DataInputStream也是一個包裝類,他提供了與機器無關方式從底層輸入流中讀取的功能。
咱們來看下以BufferdInputStream實現數據讀取的例子。
public class DecoratorTest { public static void main(String[] args) { InputStream in = null; BufferedInputStream bi = null; try { in = new FileInputStream("E:\\decorator.txt"); //in 是一個文件輸入流,經過BufferedInputStream給它添加從緩衝區讀取數據的功能。 //BufferedInputStream就是一個包裝類 bi = new BufferedInputStream(in); int count = 0; byte[] buffer = new byte[1024]; while ((count = bi.read(buffer)) != -1) { System.out.println(new String(buffer, 0, count)); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (bi != null) { bi.close(); } if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
我本身以前學習IO流的時候,看到這麼多種類的流也是很難理解,當我以設計模式角度去解讀這些流時,會更加容易理解。裝飾者模式的實現對於使用者是透明的,咱們必須瞭解這個裝飾者是什麼功能,才能恰當的使用這個類。