❝本文是設計模式系列的第三篇文章,今天主要學習裝飾者模式。
❞
不知道大家沒有有這樣一種感覺,在看書學習時,感覺都看懂了,可是過一段時間就忘,因此我們從開頭就先問自己幾個問題,過一段時間就回過頭了複習下這幾個問題,從而鞏固學到的知識,在你的大腦中將這些知識點串起來。 希望能不斷反覆的思考,將點成線,最終形成知識塊,消化掉它。
帶着問題學習
什麼是裝飾者模式? 什麼場景下需要使用裝飾者模式? 如何實現裝飾者模式? 常用框架或源碼中有哪些案例可以體現?
裝飾者模式的概念
我們先來看看裝飾者模式的說明:
❝「裝飾者模式」 動態的將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
❞
「新的設計原則」:類應該對擴展開放對修改關閉。
上面的定義雖然說明了裝飾者模式的 “角色”,但是沒有說明怎麼在我們的實現中實際 “應用”它。
接下來我們學習下它的類圖,結合着類圖在仔細分析它:
從上面的類圖,我們再來理解下裝飾者模式
裝飾者和被裝飾對象有相同的超類型 你可以用一個或多個裝飾者包裝一個對象(component的具體組件) 既然裝飾者和被裝飾對象有相同的超類型,所以在任何需要原始對象(被包裝)的場合,可以用裝飾過的對象代替它 「裝飾者可以在所委託被裝飾者的行爲 之前或者之後,加上自己的行爲,已達到特定的目的」,後面我們在如何使用中會明確的看到這點 對象可以在任何時候被裝飾,所以可以在運行時動態地、不限量地用你喜歡的裝飾者來裝飾對象
使用場景
現在有一個咖啡店,可以售賣幾種不同的咖啡,比如 摩卡、卡布奇諾、瑪奇朵、康巴納等。而且每一種具體的咖啡還可以添加不同的調料,比如奶泡、焦糖、豆漿、摩卡等,要求加不同調料最終價格不同,這個場景如果使用 OO 思想來設計,你會怎麼做呢?
毫無疑問這個場景非常適合裝飾者模式,首先被裝飾者就是我們的咖啡品種,而裝飾者就是我們的不同調料,這樣在計算價格時可以一層層去委託最終得到結果,話不多說來看如果用裝飾者模式來裝飾我們的咖啡。
看了上面我們舉例的星巴克咖啡的例子,有沒有和裝飾者模式的類圖框架對應呢? 大家可以仔細思考下。
鞏固擴展
利用上面你的裝飾者模式現在出一個場景,我們測試下到底能不能在不改動現有代碼的前提下實現,體驗下設計模式的魅力。
「點一杯雙倍摩卡豆漿奶泡拿鐵咖啡?」
是不是很方便,我們利用裝飾者模式進行組合擴展,體驗到魅力了吧。。
具體實現
下面一我們一起結合上面的例子,看下代碼實現:
// 飲料的基礎類,即component
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
// cost必須在子類實現
public abstract double cost();
}
調料抽象類,即裝飾者類:
// 調料抽象類即裝飾者類,這個類必須要能替換 Beverage,所以要繼承自 Beverage 類
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
現在有了基類,下面是一個具體的飲料:
// 藍山
public class BlueMountainCoffee extends Beverage {
public BlueMountainCoffee() {
description = "BlueMountainCoffee";
}
@Override
public double cost() {
return 0;
}
}
// 卡布奇諾
public class Cappuccino extends Beverage {
public Cappuccino() {
description = "Cappuccino";
}
@Override
public double cost() {
return 23;
}
}
// 意式濃縮咖啡
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
@Override
public double cost() {
return 25;
}
}
// 拿鐵
public class Latte extends Beverage {
public Latte() {
description = "Latte";
}
@Override
public double cost() {
return .89;
}
}
現在已經有了具體組件和抽象組件,對比裝飾者模式類圖我們實現具體的裝飾者:
// 摩卡是一個裝飾者,所以擴展自 CondimentDecorator
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Mocha";
}
// 首先調用委託被裝飾者對象,以計算價錢,然後再加上Mocha價錢
@Override
public double cost() {
return .20 + beverage.cost();
}
}
// 豆漿
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Soy";
}
@Override
public double cost() {
return 2.0 + beverage.cost();
}
}
接下來就是展示裝飾者模式魅力的時候:
// 測試類
public class StarbuzzCoffee {
public static void main(String[] args) {
// 一杯Espresso,不加調料
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + "$" + beverage.cost());
// 一杯加摩卡和豆漿的藍山咖啡
Beverage beverage1 = new BlueMountainCoffee();
beverage1 = new Mocha(beverage1);
beverage1 = new Soy(beverage1);
System.out.println(beverage1.getDescription() + "$" + beverage1.cost());
}
}
目前我們創建對象還都是硬編碼 new 出來的,不太友好,隨着後續我們學習了工廠模式就好了,持續學習吧。
現實中的裝飾者
這塊列舉些平時用到的 jdk 中的裝飾者模式體現
Java I/O
列出的順序是從裝飾者 -> 被裝飾者
LineNumberInputStream -> BufferedInputStream -> FileInputStream
一目瞭然吧,這個和我們上面講的裝飾者模式類圖基本上是一致的,相信在你再次閱讀 Jvaa I/O 包中的類時,你一定會發出 “哇” 的一聲驚歎。
全文完!fighting
原創真心不易,希望你能幫我個小忙唄,如果本文內容你覺得有所收穫,請幫忙點個“在看”唄,或者轉發分享讓更多的小夥伴看到。