8.java設計模式之裝飾者模式
阿新 • • 發佈:2020-11-18
#### 基本需求:
* 咖啡的種類有很多種,調料也有很多種,下單時,可以點單品咖啡也可以點單品咖啡+調料的組合,並計算下單時花費的金額
#### 傳統方式:
* 方式一
* 建立一個抽象類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;
>
> ......
>}
>```
>
>
#### 注意事項:
* 裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴充套件一個實現類的功能,多層裝飾比較複雜
* 擴充套件一個類的功能,動態增加功能,動