Java設計模式之裝飾者模式


要實現裝飾者模式, 注意一下幾點內容:
1.裝飾者類要實現真實類 同樣的接口
2.裝飾者類內有一個 真實對象的引用(可以通過裝飾者類的構造器傳入)
3.裝飾類對象在主類中接受請求,將 請求發送給真實的對象(相當於已經將引用傳遞到了裝飾類的真實對象)
4.裝飾者可以在傳入真實對象后, 增加一些附加功能(因為裝飾對象和真實對象都有同樣的方法,裝飾對象可以添加一定操作在調用真實對象的方法,或者先調用真實對象的方法,再添加自己的方法)
5.不用繼承,
 
先用實例說話,最后再具體裝飾者模式
 
假設要制造添加甜蜜素和着色劑的饅頭:
1.需要生產一個正常饅頭
2.為節省成本(不使用玉米面),使用染色劑加入到正常饅頭中
3.和面,最后生產出染色饅頭
 
一..先實現做面包的接口
    IBread接口包括准備材料,和面,蒸饅頭,加工饅頭(即調用前面三個步驟)
 1 package 裝飾模式;
 2 
 3 public interface IBread {
 4 
 5     public void prepair();
 6     
 7     public void kneadFlour();
 8     
 9     public void steamed();
10     
11     public void process();
12 }

 

 
 
二.制作正常饅頭
 1 package 裝飾模式;
 2 
 3 public class NormalBread implements IBread{
 4 
 5     @Override
 6     public void prepair() {
 7         
 8         System.out.println("准備面粉,水以及發酵粉...");
 9     }
10 
11     @Override
12     public void kneadFlour() {
13         
14         System.out.println("和面...");
15     }
16 
17     @Override
18     public void steamed() {
19 
20         System.out.println("蒸饅頭...香噴噴的饅頭出爐了");
21     }
22 
23     @Override
24     public void process() {
25 
26         prepair();
27         kneadFlour();
28         steamed();
29     }
30 
31 }

 

 
三.定義出制作面包的抽象類
    抽象類實現了IBread這個制作面包的接口,同時包含IBread接口的實例
    對應上述的第2個注意點:裝飾者類內有一個真實對象的引用
 1 package 裝飾模式;
 2 
 3 public abstract class AbstractBread implements IBread {
 4 
 5     private final IBread bread;
 6     
 7     public AbstractBread(IBread bread) {
 8         super();
 9         this.bread = bread;
10     }
11     @Override
12     public void prepair() {
13         this.bread.prepair();
14     }
15     @Override
16     public void kneadFlour() {
17         this.bread.kneadFlour();
18     }
19     @Override
20     public void steamed() {
21         this.bread.steamed();
22     }
23 
24     @Override
25     public void process() {
26         prepair();
27         kneadFlour();
28         steamed();
29     }
30 
31 }

 

 
 
 
四.生產有着色劑的"玉米饅頭"
    繼承AbstarctBread類,所以可以有選擇的覆蓋正常生產饅頭的方法,並添加原有方法原來的信息,同時也可以添加自己的方法
    裝飾者模式中這里最關鍵,
      對應上述的第1個注意點:裝飾者類要實現真實類 同樣的接口
 1 package 裝飾模式;
 2 
 3 public class CornDecorator extends AbstractBread{
 4 
 5     public CornDecorator(IBread bread) {
 6         
 7         super(bread);
 8     }
 9 
10     public void paint(){
11         
12         System.out.println("添加檸檬黃的着色劑");
13     }
14     @Override
15     public void kneadFlour() {
16         //添加着色劑后和面
17         this.paint();
18         super.kneadFlour();
19     }
20 
21     
22 }

 

 
 
五.生產有甜蜜素的"甜饅頭"
    實現與第四部一樣
 1 package 裝飾模式;
 2 
 3 public class SweetDecorator extends AbstractBread {
 4 
 5     public SweetDecorator(IBread bread) {
 6         
 7         super(bread);
 8     }
 9 
10     public void paint(){
11         
12         System.out.println("添加甜蜜素...");
13     }
14     
15     @Override
16     public void kneadFlour() {
17         //添加甜蜜素后和面
18         this.paint();
19         super.kneadFlour();
20     }
21     
22 }

 

 
六.開始制作添加甜蜜素和着色劑的饅頭
 1 package 裝飾模式;
 2 
 3 public class Client {
 4 
 5     public static void main(String[] args) {
 6         
 7         System.out.println("=======開始裝飾饅頭");
 8         IBread normalBread = new NormalBread();
 9         normalBread = new SweetDecorator(normalBread);
10         normalBread = new CornDecorator(normalBread);
11         normalBread.process();
12         System.out.println("=======裝飾饅頭結束");
13     }
14 }
 
七.輸出
1 =======開始裝飾饅頭
2 准備面粉,水以及發酵粉...
3 添加檸檬黃的着色劑
4 添加甜蜜素...
5 和面...
6 蒸饅頭...香噴噴的饅頭出爐了
7 =======裝飾饅頭結束

 

裝飾者模式中的4個角色
(1)被裝飾者抽象Component:是一個接口或者抽象類,定義最核心的對象,這個類是裝飾者的基類,例如IBread接口
(2)被裝飾者具體實現ConcreteComponent:這是Component接口或抽象類的實現,例如本例中的NormalBread
(3)裝飾者Decorator:一般是抽象類,實現Component,它里面必然有一個指向Component的引用,例如本例中AbstractBread
(4)裝飾者實現ConcreteDecorator1和ConcreteDecorator2:用來裝飾最基本的類,如本例中的CornDecorator,
 
JDK中的裝飾者模式
 java.io中很多使用了裝飾者模式
舉個例子:FilterInputStream繼承(實現)了InputStream,同時,BufferedInputStream繼承了FilterInputStream,
1,被裝飾者抽象組件:即最頂層的基類InputStream
2.被裝飾者具體實現ConcreteComponent:FileInputStream和FileOutputStream就是它的實現
3.裝飾者Decorator:FilterInputStream中有一個InputStream的實例和構造方法傳入InputStream對象
    protected volatile InputStream in;  
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }  
4.裝飾者實現:在   BufferedInputStream  中有構造方法傳入InputStream對象,實現了裝飾
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }  
這個構造方法,對比上面的做面包流程,可以驚奇的發現是一模一樣的. (可以將)
1.InputStream-->IBread
    (這里就是InputStream,沒什么好說的)
2.FileInputStream-->NormalBread
3.FilterInputStream-->AbstractBread
  (實現Component,這里是InputStram.它里面必然有一個指向Component的引用,這個引用就是InputStream的實例)
4. BufferedInputStream  --> CornDecorator
    (裝飾者實現,:用來裝飾最基本的類,對I傳入的nputStream進行了裝飾)
  (BufferedInputStream 的super(in)就像CornDecorator中的kneadFlour()方法,也有 super.kneadFlour();,只是這里IO流中用在了構造方法 )
這就是JDK中的裝飾者模式
 
 
 
不用繼承方式實現裝飾者模式的原因(以此例為說明對象,)
1.如果只是單獨的添加色素或者甜蜜素確實是可以做到的,只需要將 CornDecorator繼承NormalBreadSweetDecorator 繼承NormalBread ,這樣也能夠覆蓋正常制作面包的流程,添加附加的功能實現單獨制作"玉米饅頭"和"甜饅頭".
2.如此一來,如果我們要制作甜玉米饅頭(這里加點先添加甜色素,再添加玉米色素),只需要先SweetDecorator繼承 NormalBread,然后Corn SweetDecorator 再用繼承 CornDecorator ,這樣似乎是沒有問題的.
2.但是想想以下的情況,如果我們 希望能夠在添加在添加甜色素和玉米色素中間還要加入洋蔥,這要怎么做,難道說又用SweetDecorator繼承 NormalBread,然后OnionSweetDecorator繼承SweetDecorator,最后再用CornOnionSweetDecorator 繼承OnionSweetDecorator??
顯然是不可能的,這樣會導致原來代碼的復用性低,而且形成了冗余的繼承體系
4.使用上述實例的方法完全克服了這個問題,要實現添加洋蔥,只需要實現和 SweetDecorator 類似的步驟即可,最后在Client類中傳入就可以實現這個功能
 
 
使用場合
1.需要為某個現有對象添加一個新的功能或職責時,可以考慮使用裝飾者模式
2.某個對象的職責經常發生變化或經常需要動態添加職責,避免為了適應這種變化造成的繼承擴展方式
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM