裝飾器模式主要對現有的類對象進行包裹和封裝,以指望在不改變類對象及其類定義的狀況下,爲對象添加額外功能。是一種對象結構型模式。須要注意的是,該過程是經過調用被包裹以後的對象完成功能添加的,而不是直接修改現有對象的行爲,至關於增長了中間層。相似於python中的@裝飾器。html
下面仍是按照老規矩,先來了解一下該模式相關的概念和原理,而後經過兩個具體的實例體會一下如何在實際開發中應用該模式。python
能夠動態的爲同一類的不一樣對象加以修飾以添加新的功能。網絡
靈活的對類對象功能進行擴展。app
優勢:tcp
缺點:ide
下面是GoF介紹的典型的裝飾器模式的UML類圖:函數
Component:this
對象的接口類,定義裝飾對象和被裝飾對象的共同接口;spa
ConcreteComponent:code
被裝飾對象的類定義;
Decorator:
裝飾對象的抽象類,持有一個具體的被修飾對象,並實現接口類繼承的公共接口;
ConcreteDecorator:
具體的裝飾器,負責往被裝飾對象添加額外的功能;
說明:
因爲這個模式從實際的例子來理解更加的直觀方便,所以這裏再也不單獨的實現上面的UML結構代碼。
先來經過一個簡單的畫圖的實例來直觀感覺一下。
前提:
系統中存在一個畫圓的類,該類只是用來畫圓,以及其餘一些大小和位置等參數的控制。
新加需求:
這個需求的常規方法實現可能以下:
上面的兩個方法都是可行的,也是比較直觀的,這裏咱們嘗試使用裝飾器模式來實現,做爲以上兩種方法的對比。
下面來看一下裝飾器模式實現該需求的UML類圖:
接口類:shape
public interface Shape { void draw(); }
畫圓類:Circle
public class Circle implements Shape { @Override public void draw() { System.out.print("a circle!"); } }
抽象裝飾器類:Decorator
public abstract class Decorator implements Shape { protected Shape circle; public Decorator(Shape shape) { circle = shape; } public void draw() { circle.draw(); } }
爲圓邊着色裝飾器類:CircleEdge
public class CircleEdge extends Decorator { public CircleEdge(Shape circle) { super(circle); } private void setEdgeColor() { System.out.print(", edge with color"); } public void draw() { circle.draw(); setEdgeColor(); } }
爲圓填充顏色裝飾器類:CircleEdge
public class CircleFill extends Decorator { public CircleFill(Shape circle) { super(circle); } private void setEdgeFill() { System.out.print(", content with color"); } public void draw() { circle.draw(); setEdgeFill(); } }
演示:
public class Demo { public static void main(String[] args) { Shape circle = new Circle(); circle.draw(); System.out.println(""); Decorator circleEdge = new CircleEdge(circle); circleEdge.draw(); System.out.println(""); Decorator circleFill = new CircleFill(circle); circleFill.draw(); System.out.println(""); Decorator circleEdgeFill = new CircleFill(circleEdge); circleEdgeFill.draw(); } }
結果:
a circle! a circle!, edge with color a circle!, content with color a circle!, edge with color, content with color
上面咱們經過實現兩個裝飾器分別完成對邊着色和填充的需求,經過對裝飾器的進一步裝飾,咱們完成了同時着色的需求。
接下來咱們在使用網絡數據傳輸的例子來體會一下裝飾器模式,下圖表示的是應用層的文件傳輸協議FTP經過TCP來傳輸數據:
雖然應用層能夠越過傳輸層直接使用網絡層進行數據發送(如,ICMP),但多數都會使用傳輸層的TCP或者UDP進行數據傳輸的。
下面咱們用裝飾器模式來表示一下應用層數據經過傳輸層來發送數據,UML類圖以下:
上述圖中表示了,應用層的數據經過添加TCP頭或者UDP頭,而後經過下面的網絡層send數據。
數據報接口類:Datagram
public interface Datagram { void send(); // 經過網絡層發送IP數據報 }
應用層數據類:AppDatagram
public class AppDatagram implements Datagram { @Override public void send() { System.out.print("send IP datagram!"); } }
傳輸層類(抽象裝飾器):TransportLayer
public abstract class TransportLayer implements Datagram { protected Datagram appData; public TransportLayer(Datagram appData) { this.appData = appData; } public void send() { appData.send(); } }
添加TCP頭部類:UseTCP
public class UseTCP extends TransportLayer { public UseTCP(Datagram appData) { super(appData); } private void addHeader() { System.out.print("Appdata add TCP header, "); } public void send() { addHeader(); appData.send(); } }
添加TCP頭部類:UseUDP
public class UseUDP extends TransportLayer { public UseUDP(Datagram appData) { super(appData); } private void addHeader() { System.out.print("Appdata add UDP header, "); } public void send() { addHeader(); appData.send(); } }
演示:
public class Demo { public static void main(String[] args) { Datagram appData = new AppDatagram(); appData.send(); System.out.println(""); TransportLayer tcpData = new UseTCP(appData); tcpData.send(); System.out.println(""); TransportLayer udpData = new UseUDP(appData); udpData.send(); System.out.println(""); } }
結果:
send IP datagram! Appdata add TCP header, send IP datagram! Appdata add UDP header, send IP datagram!
固然這裏例子中已經添加過TCP頭部的數據報不能再使用UDP傳輸了,無心義,也被必要。
其實所謂裝飾器,本質上是對現有類對象的包裹,獲得一個增強版的對象。
和python中@裝飾器不一樣的是:
經過上面的兩個例子,應該對裝飾器模式有了一個簡單的認識。
另外,要體會到何時用繼承何時用裝飾器。
參考:
GoF《Design Patterns: Elements of Reusable Object-Oriented Software》
https://www.runoob.com/design-pattern/decorator-pattern.html