軟件設計模式修煉 -- 裝飾模式


裝飾者模式是一種用於替代繼承的技術,它經過一種無須定義子類的方式給對象動態增長職責,使對象之間的關聯關係取代類之間的繼承關係。java


模式動機

裝飾者能夠在不改變一個對象自己的基礎上給對象增長額外的新行爲,如一張照片,能夠不改變照片自己,給她增長一個相框,使得它具備防潮功能,並且用戶能夠根據須要增長不一樣類型的相框。在軟件開發中,相似給照片增長相框的狀況隨處可見,如給一個圖形界面構件增長邊框、滾動等新特性。通常有兩種方式實現給一個類或對象增長行爲:算法

  1. 繼承機制編程

    經過繼承一個類現有類可使子類在擁有自身方法的同時還擁有父類方法。但這種方式是靜態的,用戶不能控制增長行爲的方式和時機。數組

  2. 關聯機制緩存

    將一個類的對象嵌入另外一個新對象中,由另外一個對象來決定是否調用嵌入對象的行爲並擴展本身的行爲,咱們稱這個新對象(即另外一個對象)爲裝飾類(Dectorator)。app


模式定義

動態地給一個對象增長一些額外的職責(Responsibility),就增長對象功能來講,裝飾者模式比生成子類對象實現更靈活。其別名也能夠稱爲包裝器(Wrapper)。ide


模式結構

在這裏插入圖片描述

  1. Component(抽象構件)函數

    抽象構件定義了對象的接口,能夠給這些對象動態增長職責(方法)。抽象構件是具體構件和和抽象裝飾類的共同父類,聲明瞭在具體構件中實現的業務方法。測試

  2. ConcreteComponent(具體構件)this

    具體構件定義了具體構件對象,實如今抽象構件中聲明的方法。

  3. Decorator(抽象裝飾類)

    抽象裝飾類是抽象構件類的子類,用於給具體構件增長職責,但具體職責在其子類中實現。它維護一個指向抽象構件對象的引用,經過該引用調用裝飾以前構件對象的方法,並經過子類擴展該方法。

  4. ConcreteDecorator(具體裝飾類)

    具體裝飾類是抽象裝飾類的子類,負責向構件添加新的職責。每個具體裝飾類都定義了一些新行爲,它能夠調用在抽象裝飾類中定義的方法,並增長新的方法以擴充對象的行爲。


實例之多重加密系統

某系統提供一個數據加密功能,能夠對字符串進行加密。該系統分別提供了簡單的移位加密算法、稍複雜的逆向輸出加密和更高級的求模加密。用戶先使用最簡單的移位加密算法對字符串進行加密,若是以爲不夠能夠對加密後結果進行二次乃至三次加密。
在這裏插入圖片描述

  1. 抽象構件類 Cipher(抽象加密類)
    public interface Cipher {
    	
        //方法爲待加密字符串,返回值爲加密後密文
        public String encrypt(String plantTetx);
    }
  2. 具體構件類 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;
        }
    }
  3. 抽象裝飾類 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);
        }
    }
  4. 具體裝飾類 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;
        }
    }
  5. 具體裝飾類 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;
        }
    }
  6. 測試代碼 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("---------------------");
        }
    }
  7. 運行結果

    在這裏插入圖片描述


模式優缺點

裝飾模式優勢以下:

  1. 在擴展對象功能方面,裝飾者模式比繼承模式更具靈活性
  2. 能夠經過動態的方式擴展對象功能,經過配置文件在運行時選擇不一樣的裝飾器,從而實現不一樣的行爲
  3. 可使用多個具體裝飾類裝飾同一對象,獲得功能更強大的對象
  4. 用戶根據須要添加新的具體構件類和具體裝飾類,原有代碼無需改變,符合開閉原則

裝飾模式缺點以下:

  1. 使用裝飾者模式進行系統設計將產生不少小對象與裝飾類,增長了系統的複雜度
  2. 程序更加易於出錯,排查錯誤也更加困難

模式適用場景

如下狀況能夠考慮使用裝飾模式

  1. 在不影響其餘對象的狀況下,以透明、動態的方式給單個對象添加職責
  2. 當不能採用繼承對系統進行擴充或者採用繼承不利於系統擴展和維護時。不能採用繼承的狀況主要有兩類:第一類是系統存在大量獨立的擴展,爲支持每一種組合將產生大量子類;第二類是由於類不能繼承(final 類)

裝飾模式的簡化

大多數狀況下,裝飾模式的實現比標準的結構圖要簡單,能夠對裝飾模式進行簡化。簡化過程當中要注意以下幾個問題:

  1. 一個裝飾類的接口必須與被裝飾類接口保持相同。對於客戶端來講,不管是裝飾以前的對象仍是裝飾以後的對象均可以同等對待

  2. 不要把太多的邏輯和狀態放在具體構件類中,能夠經過裝飾類進行擴展

  3. 若是隻有一個具體構件類而沒有抽象構件類,那麼抽象裝飾類能夠做爲具體構件類的子類
    在這裏插入圖片描述
    若是隻有一個具體裝飾類,那也就不必再設計一個單獨的抽象裝飾類,能夠把抽象裝飾類和具體裝飾類的職責合併在一個類中
    在這裏插入圖片描述


透明裝飾模式和半透明裝飾模式

在透明裝飾模式中,要求客戶端徹底針對抽象編程,裝飾模式的透明性要求客戶端程序不該該聲明具體構件類型和具體裝飾類型,而應所有聲明爲抽象構件類型。如上述加密系統實例就是透明裝飾模式

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 = new byte[1024];
inBS.read(data);

在 Java IO 中,不只 InputStream 用到了裝飾模式,OutputStream、Reader、Writer 等都用到了此模式。

相關文章
相關標籤/搜索