1. 程式人生 > >8.java設計模式之裝飾者模式

8.java設計模式之裝飾者模式

#### 基本需求: * 咖啡的種類有很多種,調料也有很多種,下單時,可以點單品咖啡也可以點單品咖啡+調料的組合,並計算下單時花費的金額 #### 傳統方式: * 方式一 * 建立一個抽象類Drink,讓所有的單品咖啡和組合咖啡都繼承Drink類(組合很多) * UML類圖 * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118145849516-199035940.png) * 如果新增加一個單品咖啡或者調料,類的數量就會倍增,產生類爆炸 * 方式二 * 建立一個抽象類Drink,在Drink類中將所有的調料聚合進去,再讓我們的單品咖啡繼承Drink類 * UML類圖 * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118145904495-917862373.png) * 此種方式解決了類爆炸的問題,但是新增或者刪除調料時,需要修改Drink類中的內容,違反了ocp原則 #### 基本介紹: * 動態的將新功能加到物件上,不改變其結構,屬於結構型模式 * UML類圖(原理圖) * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118145920988-1594029661.png) * 裝飾者模式就像打包一個快遞 * 主體為打包的東西(Component)-> 被裝飾者 * 包裝的東西(Decorator)-> 裝飾者 * Drink為主體(Component) * 單品咖啡為具體的主體(ConcreteComponent) * 調料為裝飾者(Decorator) * 如果ConcreteComponent類很多,還可以向上抽取一個緩衝層 * 讓裝飾者和具體的主體繼承同一類可以對同一個具體的主體,進行多次包裝,通過遞迴的方式 * 如果ConcreteComponent和Decorator不是繼承同一類,則用遞迴不容易,Decorator不僅需要維護自身還需維護ConcreteComponent的變數(不一定行),繼承同一個抽象類,只需要在Decorator中維護一個抽象父類即可 * 例如:點兩個chocolate和一個milk + Espresso咖啡,示例 * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118145934869-1717786760.png) * UML類圖(案例) * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118145945747-1296317657.png) * 程式碼實現 * >```java >@Data >public abstract class Drink { > > // Component類 ConcreteComponent類和Decorator類都繼承該類 > > private String description; > > private float price = 0f; > > public abstract float cost(); > >} >``` > > * >```java >public abstract class Coffee extends Drink{ > > // 對具體的被裝飾者進行了緩衝設計 > @Override > public float cost() { > return super.getPrice(); > } > >} > >// 子類一 義大利咖啡 >class Espresso extends Coffee { > > public Espresso() { > super.setDescription("Espresso"); > super.setPrice(3.0f); > } > >} > >// 子類二 濃縮咖啡 >class ShortBlack extends Coffee { > > public ShortBlack() { > super.setDescription("ShortBlack"); > super.setPrice(5.0f); > } > >} >``` > > * >```java >public abstract class Decorator extends Drink { > > // 對裝飾者進行了緩衝設計 > > // 聚合被裝飾者 > protected Drink drink; > > public Decorator(Drink drink) { > this.drink = drink; > } > > @Override > public float cost() { > // 金額除自己的金額外還需要加上被裝飾者的金額 > return this.drink.cost() + super.getPrice(); > } > > @Override > public String getDescription() { > // 輸出被裝飾者的資訊 > return this.drink.getDescription() + "&" + super.getDescription(); > } >} > >// 子類一 牛奶 >class Milk extends Decorator { > > public Milk(Drink drink) { > super(drink); > super.setDescription("Milk"); > super.setPrice(1f); > } > >} > >// 子類二 巧克力 >class Chocolate extends Decorator { > > public Chocolate(Drink drink) { > super(drink); > super.setDescription("Chocolate"); > super.setPrice(2f); > } > >} >``` > > * >```java >public class Client { > public static void main(String[] args) { > // 單點義大利咖啡 + Milk > Drink order = new Espresso(); > System.out.println("總費用:" + order.cost()); > System.out.println("描述:" + order.getDescription()); > // 新增一份 Milk > order = new Milk(order); > System.out.println("總費用:" + order.cost()); > System.out.println("描述:" + order.getDescription()); > // 新增第一份 Chocolate > order = new Chocolate(order); > System.out.println("總費用:" + order.cost()); > System.out.println("描述:" + order.getDescription()); > // 新增第二份 Chocolate > order = new Chocolate(order); > System.out.println("總費用:" + order.cost()); > System.out.println("描述:" + order.getDescription()); > } >} >``` > > * 如果再增加單品咖啡或者調料時,只需繼承Coffee或者Decorator類即可,系統的擴充套件性提高了,解決了類爆炸,體現了ocp原則,動態的為被裝飾的類新增功能 #### jdk原始碼: * 在jdk的io原始碼中就使用到的裝飾者模式 * UMl類圖 * ![](https://img2020.cnblogs.com/blog/2093590/202011/2093590-20201118150016073-895512470.png) * InputStream作為抽象父類,相當於Drink類 * FileInputStream、StringBufferInputStream、ByteArrayInputStream繼承了InputStream相當於咖啡單品(並沒有再進行抽象),被裝飾者 * FilterInputStream繼承了InputSteam並且內部維護了一個其父類InputStream相當於一個Decorator裝飾者 * BufferInputStream、DataInputStream、LineNumberInputStream繼承了FilterInputStream,相當於Milk、ChocolateD等,為裝飾者的子類 * 關鍵程式碼 * >```java >public class FilterInputStream extends InputStream { > /** > * The input stream to be filtered. > */ > protected volatile InputStream in; > > ...... >} >``` > > #### 注意事項: * 裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴充套件一個實現類的功能,多層裝飾比較複雜 * 擴充套件一個類的功能,動態增加功能,動