裝飾者模式可以動態地將功能附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的代替方案。java
聽懂了這句話就不用往下看了,說明你會了。算法
聽不懂我以爲也正常,若是用一句話能學會就沒人看書了。像我這種笨人,都是學會了一個模式,而後往它的定義上套。安全
在說明何時使用裝飾者模式以前,先舉一個咱們平時都在使用的例子。框架
Java的I/O框架中就大量的使用了裝飾者模式,例如《Head First》中所說的:函數
如上圖,裝飾者從表面上看就是有一個原始的被裝飾者對象,而後可能通過多層的裝飾附加不少新的功能,還能不修改被裝飾者。測試
雖然繼承關係也能達到這種預期,可是Java中的繼承是很寶貴的,由於一個類只能繼承一個父類。有可能父類已經用於模版方法或者其餘用途了。this
裝飾者能夠簡介的避免繼承濫用的問題。因此使用了裝飾者模式,可以在不修改任何底層代碼的狀況下,給你的(或別人的)對象賦予新的功能。加密
總結一下何時須要使用裝飾者:spa
以上是可使用裝飾者模式的場景,能夠還不是很清晰,下面舉個例子來看看如何使用裝飾界解決問題,並舉一個不使用裝飾者時的反面教材。設計
以一個簡單的需求來實現裝飾者模式,要求作一個通用的API解密組件,實現2種功能:
需求很常見,大多API調用作安全策略時,給咱們的數據都是加密的,咱們須要經過解密算法拿到數據的明文,而後大多數作法是將參數根據名稱排序,放到一個字符串中,經過算法進行加密,而後與數據中的一個sign屬性的值進行對比,若是一直表明驗證簽名經過,就能夠證實本次請求是安全的。
這裏主要將加密和驗證簽名兩個動做拿出來看成需求,具體實現不進行實現。
設計一個多級的繼承類,RSADecode和AESDecode這一層實現了普通的RSA或AES解密,下一層實現了幾種解密和驗籤方法。
這種設計有個問題,那就是容易類爆炸,每當多一種新的實現方式,都要成倍的增長類的數量,,例如加一個Base64的Decode,須要加3個類(Base64Decode類,Base64AndSHADecode類,Base64AndMD5Decode類),實現越多,須要加的類就越多,這和橋接模式中的類爆炸緣由是同樣的。
能夠把須要的算法,以布爾值的方式聲明到父類中,而後在子類經過if,else進行實現。這樣就不會有類爆炸的弊端。先來看代碼:
public abstract class Decode { boolean isRSA; boolean isAES; abstract void decode(); }
作一個父類,其中聲明解密須要的算法的布爾值,以及一個抽象方法。
public class DecodeImpl extends Decode { boolean isMD5; boolean isSHA; void decode() { if (isRSA) { // 進行RSA操做 } else if (isAES) { // 進行AES操做 } } }
解密的對象,實現抽象方法,而後判斷布爾值來執行具體的解密操做。隨便定義好驗證簽名須要的布爾值。
public class Sign extends DecodeImpl { void decode() { super.decode(); if (isAES) { // 進行RSA操做 } else if (isMD5) { // 進行AES操做 } } }
最後定義驗證簽名的對象,作法根解密對象是同樣的,先調用解密方法,而後根據布爾值執行驗證簽名的業務。
這麼作會有幾個問題:
如圖所示,這就是裝飾者模式的實現類圖:
具體的代碼以下:
/** * 裝飾者模式父級組件。 */ public abstract class Component { public abstract void decode(final String data); } // ----------------------------------------------------------------- // 這裏將幾種解密的方法做爲被裝飾者,被裝飾者繼承了Component對象 // ----------------------------------------------------------------- /** * 被裝飾者。 */ public class RSADecode extends Component { public void decode(final String data) { System.out.println("RSA解密:" + data); } } /** * 被裝飾者。 */ public class AESDecode extends Component { public void decode(final String data) { System.out.println("AES解密:" + data); } } // ----------------------------------------------------------------- // 這裏將驗證簽名做爲裝飾者,也繼承了Component對象。 // 另外,裝飾者父類中,還定義了被裝飾者,須要經過構造函數將它傳遞進來。 // ----------------------------------------------------------------- /** * 裝飾者抽象類。 */ public abstract class ValidateSign extends Component { private Component component; /** * 在構造函數中傳入被裝飾者。 */ public ValidateSign(Component component) { this.component = component; } public Component getComponent() { return this.component; } } /** * 裝飾者。 */ public class SHASign extends ValidateSign { public SHASign(Component component) { super(component); } public void decode(String data) { // 先調用被裝飾者 super.getComponent().decode(data); // 在實現裝飾者的功能 System.out.println("SHA驗籤:" + data); } } /** * 裝飾者。 */ public class MD5Sign extends ValidateSign { public MD5Sign(Component component) { super(component); } public void decode(String data) { // 先調用被裝飾者 super.getComponent().decode(data); // 在實現裝飾者的功能 System.out.println("MD5驗籤:" + data); } }
使用裝飾者模式進行業務調用:
public class Client { public static void main(String[] args) { // 普通的RSA解密 System.out.println("普通調用,不使用裝飾者模式。"); Component component = new RSADecode(); component.decode("Hello World."); System.out.println(""); // 裝飾者模式應用,用SHASign來裝飾RSADecode // 爲組件加入了SHA驗籤的能力 System.out.println("裝飾者模式調用。"); Component component1 = new SHASign(new RSADecode()); component1.decode("Hello World."); System.out.println(""); // 解密後,還能夠進行兩次驗籤,很靈活 System.out.println("裝飾者模式調用。"); Component component2 = new MD5Sign(new SHASign(new RSADecode())); component2.decode("Hello World."); } } // 輸出: 普通調用,不使用裝飾者模式。 RSA解密:Hello World. 裝飾者模式調用。 RSA解密:Hello World. SHA驗籤:Hello World. 裝飾者模式調用。 RSA解密:Hello World. SHA驗籤:Hello World. MD5驗籤:Hello World.
回顧概念:
裝飾者模式可以動態地將功能附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的代替方案。
動態添加功能:動態將功能附加到對象上,並且能夠隨意組合,在Main函數中咱們也作到了,能夠屢次對被裝飾者進行裝飾,裝飾的順序也能夠隨意調整。
比繼承有彈性:裝飾者中使用了關聯關係的方式,將被裝飾者經過構造函數傳入,創建關聯。
弊端:但它也有一些弊端,會出現不少小類。具體的使用須要根據業務場景進行權衡。
以上就是裝飾者模式的一些理解, 有不足之處請你們矯正,謝謝。