要實現裝飾者模式,
注意一下幾點內容:
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繼承NormalBread 和SweetDecorator 繼承NormalBread ,這樣也能夠覆蓋正常制作面包的流程,添加附加的功能實現單獨制作"玉米饅頭"和"甜饅頭".
2.如此一來,如果我們要制作甜玉米饅頭(這里加點先添加甜色素,再添加玉米色素),只需要先SweetDecorator繼承 NormalBread,然后Corn
SweetDecorator 再用繼承 CornDecorator ,這樣似乎是沒有問題的.
2.但是想想以下的情況,如果我們
希望能夠在添加在添加甜色素和玉米色素中間還要加入洋蔥,這要怎么做,難道說又用先SweetDecorator繼承 NormalBread,然后OnionSweetDecorator繼承SweetDecorator,最后再用CornOnionSweetDecorator 繼承OnionSweetDecorator??
顯然是不可能的,這樣會導致原來代碼的復用性低,而且形成了冗余的繼承體系
4.使用上述實例的方法完全克服了這個問題,要實現添加洋蔥,只需要實現和
SweetDecorator 類似的步驟即可,最后在Client類中傳入就可以實現這個功能
使用場合
1.需要為某個現有對象添加一個新的功能或職責時,可以考慮使用裝飾者模式
2.某個對象的職責經常發生變化或經常需要動態添加職責,避免為了適應這種變化造成的繼承擴展方式