目錄
1、場景直入
2、解決方案
3、具體代碼
4、升華總結
(1)裝飾者模式定義:
(2)使用場景:
(3)優點:
(4)缺點:
5、源碼解析
1、場景直入
有一個煎餅果子類,
動态需求有加一個蛋、加兩個蛋、加辣椒、加蔥、加香菜、還有加香腸火腿等等。不同的需求對應了不同的價格。
那我們如果針對每一種需求,都聲明一個煎餅果子的子類,那需要多少類?
煎餅加一個蛋類、煎餅加一個香腸類、煎餅加兩個蛋類、煎餅加兩個蛋兩個香腸類。。。學過排列組合的老師出來一下!
可以看到,單純使用繼承,在類似場景中會導緻類的數目爆炸式增長。
2、解決方案
核心是把蛋、香腸、辣椒等這些需求都單獨建立類,然後再和煎餅果子基礎類産生組合,組合後保證産生的類還得是煎餅果子類。這就像一個人穿的衣服,長裙、短裙、褲子襯衫就像人的裝飾,人始終是主體,一個人穿上漂亮的短裙還是人嗎?當然是。穿上超短褲還是人嗎?當然是。不穿呢?。。。
首先,有一個煎餅果子抽象類AbstractBatterCake
然後,再來一個裝飾抽象類AbstractDecorator,該類繼承自煎餅果子抽象類AbstractBatterCake,保證任何的裝飾之後,得到的仍然是煎餅果子類。另外,構造方法的參數也要是煎餅果子抽象類,即從哪個類的基礎上進行裝飾,這樣保證了裝飾類的連續可動态疊加。
其次,依次具體化實作裝飾抽象類AbstractDecorator。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9E1T1smej9mQzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcucTNwAjN0EjM1ADOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
3、具體代碼
https://github.com/phs999/DesignPatterns/tree/4f98c1fc8e1ab5cbc53533305907fd24c5ca6cca/design_pattern/src/structural/decorator/v2
一定要先看關鍵的測試類:
package structural.decorator.v2;
public class Test {
public static void main(String[] args) {
AbstractBatterCake batterCake;
batterCake=new BatterCake();
batterCake=new EggDecorator(batterCake);
batterCake=new EggDecorator(batterCake);
batterCake=new SausageDecorator(batterCake);
System.out.println(batterCake.getDesc()+" 銷售價格:"+batterCake.price());
}
}
然後是抽象煎餅類:
package structural.decorator.v2;
//抽象煎餅類
public abstract class AbstractBatterCake {
protected abstract String getDesc();
protected abstract int price();
}
基本煎餅子類:
package structural.decorator.v2;
//煎餅
public class BatterCake extends AbstractBatterCake{
@Override
protected String getDesc() {
return "煎餅";
}
@Override
protected int price() {
return 8;
}
}
裝飾抽象類:
package structural.decorator.v2;
public abstract class AbstractDecorator extends AbstractBatterCake{
private AbstractBatterCake battercake;
public AbstractDecorator(AbstractBatterCake batterCake) {
this.battercake=batterCake;
}
//public abstract void dosomthing();
@Override
protected String getDesc() {
return this.battercake.getDesc();
}
@Override
protected int price() {
return this.battercake.price();
}
}
加蛋裝飾類:
package structural.decorator.v2;
//加蛋 裝飾
public class EggDecorator extends AbstractDecorator{
public EggDecorator(AbstractBatterCake batterCake) {
super(batterCake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一個雞蛋";
}
@Override
protected int price() {
return super.price()+1;
}
}
香腸裝飾類:
package structural.decorator.v2;
//加香腸 裝飾
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(AbstractBatterCake batterCake) {
super(batterCake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一根香腸";
}
@Override
protected int price() {
// TODO Auto-generated method stub
return super.price()+2;
}
}
4、升華總結
我們從代碼中看到,在基礎的煎餅類上我們可以不斷的疊加裝飾,使之具有更加豐富的料。但是,目前的裝飾類隻是進行了對煎餅基本方法的重寫。當然,我們可以在裝飾類中新增新的方法,使得煎餅擁有更加豐富的特性。
(1)裝飾者模式定義:
在不改變原有對象的基礎之上,将功能附加到對象上。
提供了比繼承更有彈性的替代方案(擴充原有對象功能)。
(2)使用場景:
擴充一個類的功能或給一個類添加附加職責
動态的給一個類添加功能,這些功能可以再動态的撤銷。
(3)優點:
繼承的有力補充,比繼承靈活,不改變原有對象的情況下給一個對象擴充功能。
可以将多個類中重複的功能代碼單獨拿出來,實作複用,降低基礎類的複雜性。
通過使用不同的裝飾類以及這些裝飾類的排列組合,可以實作不同的效果。
符合開閉原則。
(4)缺點:
會出現更多的代碼,更多的類,增加程式複雜性。
動态裝飾時、多層裝飾時會更複雜。比如在煎餅果子的基礎上,現在又有了包子、面條等食物,這樣就會更加複雜,不過這不是設計模式本身帶來的複雜性,是業務邏輯複雜後引起的代碼複雜性。
另外,你也可以參考 裝飾者模式在坦克大戰代碼中的應用 ,文章中對裝飾者模式進行了總結,并且對實際應用中遇到的一些問題和注意事項作了介紹。
5、源碼解析
比如我們看Java IO中的BufferedReader,該類繼承自Reader抽象類,同時在該類中使用帶參構造參數Reader,傳入修飾前的類。
同樣,我們也可以看到BufferedInputStream與InputStream之間的關系。