設計模式(九)——裝飾者模式(io原始碼分析)

十四lin發表於2021-02-04

1 星巴克咖啡訂單專案(咖啡館):

1) 咖啡種類/單品咖啡:Espresso(義大利濃咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(無因咖啡)

2) 調料:Milk、Soy(豆漿)、Chocolate

3) 要求在擴充套件新的咖啡種類時,具有良好的擴充套件性、改動方便、維護方便

4) 使用 OO 的來計算不同種類咖啡的費用:  客戶可以點單品咖啡,也可以單品咖啡+調料組合。

方案 1-解決星巴克咖啡訂單專案

 

方案 1-解決星巴克咖啡訂單問題分析

1) Drink 是一個抽象類,表示飲料

2) des 就是對咖啡的描述, 比如咖啡的名字

3) cost() 方法就是計算費用,Drink 類中做成一個抽象方法.

4) Decaf 就是單品咖啡, 繼承 Drink,  並實現 cost

5) Espress && Milk 就是單品咖啡+調料, 這個組合很多

6) 問題:這樣設計,會有很多類,當我們增加一個單品咖啡,或者一個新的調料,類的數量就會倍增,就會出現類爆炸

4.1 方案 2-解決星巴克咖啡訂單(好點)

1) 前面分析到方案 1 因為咖啡單品+調料組合會造成類的倍增,因此可以做改進,將調料內建到 Drink 類,這樣就不會造成類數量過多。從而提高專案的維護性(如圖)

2) 說明: milk,soy,chocolate 可以設計為 Boolean,表示是否要新增相應的調料.

4 .2 方案 2-解決星巴克咖啡訂單問題分析

1) 方案 2 可以控制類的數量,不至於造成很多的類

2) 在增加或者刪除調料種類時,程式碼的維護量很大

3) 考慮到使用者可以新增多份 調料時,可以將 hasMilk 返回一個對應 int

4) 考慮使用 裝飾者 模式

5 裝飾者模式定義

1) 裝飾者模式:動態的將新功能附加到物件上。在物件功能擴充套件方面,它比繼承更有彈性,裝飾者模式也體現了開閉原則(ocp)

2) 這裡提到的動態的將新功能附加到物件和 ocp 原則,在後面的應用例項上會以程式碼的形式體現,請同學們注意體會。

6 裝飾者模式原理

1) 裝飾者模式就像打包一個快遞

主體:比如:陶瓷、衣服 (Component) //  被裝飾者

包裝:比如:報紙填充、塑料泡沫、紙板、木板(Decorator)

2) Component 主體:比如類似前面的 Drink

3) ConcreteComponent Decorator

ConcreteComponent:具體的主體, 比如前面的各個單品咖啡

4) Decorator: 裝飾者,比如各調料.

在如圖的 Component ConcreteComponent 之間,如果 ConcreteComponent 類很多,還可以設計一個緩衝層,

將共有的部分提取出來,抽象層一個類。

7 裝飾者模式解決星巴克咖啡訂單

8 裝飾者模式下的訂單:2 份巧克力+一份牛奶的 LongBlack

9 裝飾者模式咖啡訂單專案應用例項

Drink類
package com.lin.decorator;

public abstract class Drink {

    public String des;//描述
    private float price = 0.0f;
    
    public String getDes() {
        return des;
    }
    public void setDes(String des) {
        this.des = des;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
    
    public abstract float cost();
}
Espresso類
package com.lin.decorator;
// 義大利Coffee
public class Espresso extends Coffee {

    public Espresso() {
        super.setDes("義大利咖啡");
        super.setPrice(16.01f);
    }

}
LongBlack 類
package com.lin.decorator;

public class LongBlack extends Coffee {

    public LongBlack() {
        super.setPrice(14.0f);
        super.setDes(getPrice()+"美國咖啡");
    }
}
Coffee類
package com.lin.decorator;

public  class Coffee extends Drink{

    @Override
    public float cost() {
        return super.getPrice();
    }

    
}
Decorator 類
package com.lin.decorator;

public class Decorator extends Drink{

    private Drink obj;
    @Override
    public float cost() {
        return super.getPrice() + obj.cost();
    }

    public Decorator(Drink obj) {
        this.obj = obj;
    }
    
    @Override
    public String getDes() {
        return super.getPrice() + " " + super.getDes() + "&&" + obj.getDes()  ;
    }
    
}
Chocolate 類
package com.lin.decorator;

public class Chocolate extends Decorator{

    public Chocolate(Drink obj) {
        super(obj);
        super.setDes("巧克力");
        super.setPrice(3.0f);
        
    }

    
}

class Milk extends Decorator{
    
    public Milk(Drink obj) {
        super(obj);
        super.setDes("牛奶");
        super.setPrice(4.0f);
        
    }
}

class Sof extends Decorator{
    
    public Sof(Drink obj) {
        super(obj);
        super.setDes("豆漿");
        super.setPrice(1.0f);
        
    }
}
CoffeeStore 類
package com.lin.decorator;

public class CoffeeStore {

    /*
     *     2分巧克力+一份牛奶的LongBlack
     *     1 1份LongBlack
     *  2 1份牛奶
     *  3 2份巧克力
     */
    public static void main(String[] args) {
        Drink longBlack = new LongBlack();
        System.out.println("free:" + longBlack.cost()+"\t"+"des:"+longBlack.getDes());
        
        longBlack = new Milk(longBlack);
        System.out.println("free:" + longBlack.cost()+"\t"+"des:"+longBlack.getDes());

        longBlack = new Chocolate(longBlack);
        System.out.println("free:" + longBlack.cost()+"\t"+"des:"+longBlack.getDes());

        longBlack = new Chocolate(longBlack);
        System.out.println("free:" + longBlack.cost()+"\t"+"des:"+longBlack.getDes());
    }
}

10 裝飾者模式在 JDK 應用的原始碼分析

1. InputStream  是抽象類,  類似我們前面講的 Drink

2. FileInputStream 是 InputStream  子類,類似我們前面的 DeCaf, LongBlack

3. FilterInputStream 是 InputStream 子類:類似我們前面 的 Decorator 修飾者

4. DataInputStream   FilterInputStream  子類,具體的修飾者,類似前面的 Milk, Soy 等

5. FilterInputStream   有 protected volatile InputStream in;  即含被裝飾者

6. 分析得出在 jdk io 體系中,就是使用裝飾者模式

 

 

僅供參考,有錯誤還請指出!

有什麼想法,評論區留言,互相指教指教。

 

相關文章