裝飾者模式是一種用于替代繼承的技術,它通過一種無須定義子類的方式給物件動態增加職責,使物件之間的關聯關系取代類之間的繼承關系,
模式動機
裝飾者可以在不改變一個物件本身的基礎上給物件增加額外的新行為,如一張照片,可以不改變照片本身,給她增加一個相框,使得它具有防潮功能,而且用戶可以根據需要增加不同型別的相框,在軟體開發中,類似給照片增加相框的情況隨處可見,如給一個圖形界面構件增加邊框、滾動等新特性,一般有兩種方式實作給一個類或物件增加行為:
-
繼承機制
通過繼承一個類現有類可以使子類在擁有自身方法的同時還擁有父類方法,但這種方式是靜態的,用戶不能控制增加行為的方式和時機,
-
關聯機制
將一個類的物件嵌入另一個新物件中,由另一個物件來決定是否呼叫嵌入物件的行為并擴展自己的行為,我們稱這個新物件(即另一個物件)為裝飾類(Dectorator),
模式定義
動態地給一個物件增加一些額外的職責(Responsibility),就增加物件功能來說,裝飾者模式比生成子類物件實作更靈活,其別名也可以稱為包裝器(Wrapper),
模式結構

-
Component(抽象構件)
抽象構件定義了物件的介面,可以給這些物件動態增加職責(方法),抽象構件是具體構件和和抽象裝飾類的共同父類,宣告了在具體構件中實作的業務方法,
-
ConcreteComponent(具體構件)
具體構件定義了具體構件物件,實作在抽象構件中宣告的方法,
-
Decorator(抽象裝飾類)
抽象裝飾類是抽象構件類的子類,用于給具體構件增加職責,但具體職責在其子類中實作,它維護一個指向抽象構件物件的參考,通過該參考呼叫裝飾之前構件物件的方法,并通過子類擴展該方法,
-
ConcreteDecorator(具體裝飾類)
具體裝飾類是抽象裝飾類的子類,負責向構件添加新的職責,每一個具體裝飾類都定義了一些新行為,它可以呼叫在抽象裝飾類中定義的方法,并增加新的方法以擴充物件的行為,
實體之多重加密系統
某系統提供一個資料加密功能,可以對字串進行加密,該系統分別提供了簡單的移位加密演算法、稍復雜的逆向輸出加密和更高級的求模加密,用戶先使用最簡單的移位加密演算法對字串進行加密,如果覺得不夠可以對加密后結果進行二次乃至三次加密,

-
抽象構件類 Cipher(抽象加密類)
public interface Cipher { //方法為待加密字串,回傳值為加密后密文 public String encrypt(String plantTetx); } -
具體構件類 SimpleCipher(簡單加密類)
public class SimpleCipher implements Cipher { /* * 以凱撒加密的方式實作加密方法 */ @Override public String encrypt(String plantTetx) { String str = ""; for (int i = 0; i < plantTetx.length(); i++) { char c = plantTetx.charAt(i); if (c >= 'a' && c <= 'z') { c += 6; if (c > 'z') c -= 26; if (c < 'a') c += 26; } if (c >= 'A' && c <= 'Z') { c += 6; if(c > 'Z') c -= 26; if(c < 'A') c += 26; } str += c; } return str; } } -
抽象裝飾類 CipherDecorator(加密裝飾類)
public class CipherDecorator implements Cipher { private Cipher cipher; public CipherDecorator(Cipher cipher) { this.cipher = cipher; } @Override public String encrypt(String plantTetx) { // 呼叫 cipher 物件的 encrypt() 方法 return cipher.encrypt(plantTetx); } } -
具體裝飾類 ComplexCipher(復雜加密類)
public class ComplexCipher extends CipherDecorator { public ComplexCipher(Cipher cipher) { super(cipher); } // 呼叫了父類的 encrypt() 方法 // 并通過新增的 reserve() 方法對加密后字串做進一步處理 public String encrypt(String plainText) { String result = super.encrypt(plainText); result = reverse(result); return result; } public String reverse(String text) { String str = ""; for (int i = text.length(); i > 0; i--) { str += text.substring(i - 1, i); } return str; } } -
具體裝飾類 AdvancedCipher(高級加密類)
public class AdvancedCipher extends CipherDecorator { public AdvancedCipher(Cipher cipher) { super(cipher); } // 呼叫了父類的 encrypt() 方法 // 并通過新增的 mod() 方法對加密后字串做進一步處理 @Override public String encrypt(String plantTetx) { String result = super.encrypt(plantTetx); result = mod(result); return result; } public String mod(String text) { String str = ""; for (int i = 0; i < text.length(); i++) { String c = String.valueOf(text.charAt(i) % 6); str += c; } return str; } } -
測驗代碼 Client
public class Client { public static void main(String[] args) { String password = "sunnyLiu"; //明文 String cpassword; //密文 Cipher sc = new SimpleCipher(); cpassword = sc.encrypt(password); System.out.println(cpassword); System.out.println("---------------------"); Cipher cc = new ComplexCipher(sc); cpassword = cc.encrypt(password); System.out.println(cpassword); System.out.println("---------------------"); //可以對裝飾之后的 cc 物件繼續進行裝飾 //從而進一步對字串進行處理,獲得更復雜的加密結果 Cipher ac = new AdvancedCipher(cc); cpassword = ac.encrypt(password); System.out.println(cpassword); System.out.println("---------------------"); } } -
運行結果

模式優缺點
裝飾模式優點如下:
- 在擴展物件功能方面,裝飾者模式比繼承模式更具靈活性
- 可以通過動態的方式擴展物件功能,通過組態檔在運行時選擇不同的裝飾器,從而實作不同的行為
- 可以使用多個具體裝飾類裝飾同一物件,得到功能更強大的物件
- 用戶根據需要添加新的具體構件類和具體裝飾類,原有代碼無需改變,符合開閉原則
裝飾模式缺點如下:
- 使用裝飾者模式進行系統設計將產生很多小物件與裝飾類,增加了系統的復雜度
- 程式更加易于出錯,排查錯誤也更加困難
模式適用場景
以下情況可以考慮使用裝飾模式
- 在不影響其他物件的情況下,以透明、動態的方式給單個物件添加職責
- 當不能采用繼承對系統進行擴充或者采用繼承不利于系統擴展和維護時,不能采用繼承的情況主要有兩類:第一類是系統存在大量獨立的擴展,為支持每一種組合將產生大量子類;第二類是因為類不能繼承(final 類)
裝飾模式的簡化
大多數情況下,裝飾模式的實作比標準的結構圖要簡單,可以對裝飾模式進行簡化,簡化程序中要注意如下幾個問題:
-
一個裝飾類的介面必須與被裝飾類介面保持相同,對于客戶端來說,無論是裝飾之前的物件還是裝飾之后的物件都可以同等對待
-
不要把太多的邏輯和狀態放在具體構件類中,可以通過裝飾類進行擴展
-
如果只有一個具體構件類而沒有抽象構件類,那么抽象裝飾類可以作為具體構件類的子類

如果只有一個具體裝飾類,那也就沒必要再設計一個單獨的抽象裝飾類,可以把抽象裝飾類和具體裝飾類的職責合并在一個類中

透明裝飾模式和半透明裝飾模式
在透明裝飾模式中,要求客戶端完全針對抽象編程,裝飾模式的透明性要求客戶端程式不應該宣告具體構件型別和具體裝飾型別,而應全部宣告為抽象構件型別,如上述加密系統實體就是透明裝飾模式
Cipher sc = new SimpleCipher();
Cipher cc = new ComplexCipher(sc);
Cipher ac = new AdvancedCipher(cc);
裝飾模式的用意是在不改變介面的前提下增強原有類的功能,在增強功能時用戶往往需創建新的方法,如希望直接使用復雜加密演算法中的 reverse() 方法,這時就要采用半透明裝飾模式
SimpleCipher sc = new SimpleCipher();
ComplexCipher cc = new ComplexCipher(sc);
AdvancedCipher ac = new AdvancedCipher(cc);
Java IO 對裝飾模式的應用
這里對 IO 流不再做過多介紹,以 InputStream 和 OutputStream 為例,它們只提供了最簡單的流處理方法,只能讀入和寫出字符,沒有緩沖處理、無法處理檔案,

InputStream 是所有位元組輸入流的父類,其中宣告的讀取以及操作資料方法會被所有位元組輸入流繼承,子類中有一個 FilterInputStream 類,它又包含了一些子類,如用于給一個輸入流添加緩沖功能的 BufferedInputStream,用于讀取原始型別的資料的 DataInputStream 等,
InputStream 的層次結構對應裝飾模式的結構,其中 InputStream 是一個抽象類,它對應裝飾模式中的抽象構件類,而 FilterInputStream、ByteArrayInputStream 等都直接繼承 InputStream 類,它們實作了在 InputStream 中定義的 read() 方法,FilterInputStream 類也是 InputStream 的子類,對應抽象裝飾類的角色,
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
// 建構式需要傳遞一個 InputStream 物件的參考
// in 物件可以是任何繼承自 InputStream 型別的參考
protected FilterInputStream(InputStream in) {
this.in = in;
}
...
}
BufferInputStream 是 FilterInputStream 的子類,相當于裝飾模式中的具體裝飾類,對傳入的輸入流做不同的裝飾,BufferInputStream 類提供了一個快取機制,使用一個陣列作為資料讀入的緩沖區,并覆寫了父類的 read() 方法,在呼叫輸入流讀取資料前都會檢查快取是否已滿,實作了對輸入流物件動態添加新功能的目的,在此處的新功能即為緩沖控制,
FileInputStream inFS = new FileInputStream("temp/fileSrc.txt");
BufferedInputStream inBS = new BufferedInputStream(inFS);
//定義一個位元組陣列,用于存放緩沖資料
byte[] data = https://www.cnblogs.com/Yee-Q/p/new byte[1024];
inBS.read(data);
在 Java IO 中,不僅 InputStream 用到了裝飾模式,OutputStream、Reader、Writer 等都用到了此模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/45274.html
標籤:其他
上一篇:Maven的使用
下一篇:軟體設計模式修煉 -- 外觀模式
