裝飾器模式又叫包裝模式,資料結構型模式;是指在不改變現有物件結構的情況下,動態的給改物件增加一些職責(即增加其額外功能)的模式,
在星巴克咖啡店,有美式咖啡(LongBlack)、無因咖啡(Decaf)、意大利農咖啡(Espresso)等不同的咖啡種類,也可以添加牛奶(Milk)、豆漿(Soy)、巧克力(Chocolate)等調料,下面我們就以這個為例子講解裝飾器模式,
使用傳統的方式

使用傳統的方式設計,就是每一種咖啡和每一種調料都寫一個類,都繼承自Drink抽象類,這樣的缺點是每增加一個單品咖啡,或者增加一個新的調料,類的數量就會成倍增加,形成類爆炸,
裝飾器模式
裝飾器模式的UML類圖:

從上圖可以看到裝飾器模式主要有抽象構件角色、具體構件角色、抽象裝飾角色、具體裝飾角色等四個角色:
- 抽象構件角色:給出一個抽象介面,以規范準備接收附加責任的物件
- 具體構件角色:實作抽象構件,并通過裝飾角色為其添加一些職責
- 抽象裝飾角色:繼承抽象構件角色,并包含具體構件的實體,可以通過其子類擴展具體構件的功能
- 具體裝飾角色:實作抽象裝飾的相關方法,并給具體構件物件添加附加的責任
使用裝飾器模式完成上面例子的UML類圖:

抽象構件角色:
package com.charon.decorator;
/**
* @className: Drink
* @description:
* @author: charon
* @create: 2022-03-19 22:48
*/
public abstract class Drink {
/**
* 描述
*/
public String desc;
/**
* 價格
*/
private float price = 0.0f;
/**
* Gets the value of desc
*
* @return the value of desc
*/
public String getDesc() {
return desc;
}
/**
* Sets the desc
*
* @param desc desc
*/
public void setDesc(String desc) {
this.desc = desc;
}
/**
* Gets the value of price
*
* @return the value of price
*/
public float getPrice() {
return price;
}
/**
* Sets the price
*
* @param price price
*/
public void setPrice(float price) {
this.price = price;
}
/**
* 計算費用的方法,交給子類實作
* @return
*/
public abstract float cost();
}
具體構建角色:
package com.charon.decorator;
/**
* @className: Coffee
* @description:
* @author: charon
* @create: 2022-03-19 22:37
*/
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
package com.charon.decorator;
/**
* @className: LongBlack
* @description:
* @author: charon
* @create: 2022-03-19 23:16
*/
public class LongBlack extends Coffee{
public LongBlack() {
setDesc(" 美式咖啡 ");
setPrice(5.0f);
}
}
package com.charon.decorator;
/**
* @className: Decaf
* @description:
* @author: charon
* @create: 2022-03-19 23:11
*/
public class Decaf extends Coffee{
public Decaf() {
setDesc(" 無因咖啡 ");
setPrice(1.0f);
}
}
抽象裝飾角色:
package com.charon.decorator;
/**
* @className: Decorator
* @description:
* @author: charon
* @create: 2022-03-19 23:13
*/
public class Decorator extends Drink{
/**
* 使用聚合的方式
*/
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
return super.getPrice() + drink.cost();
}
@Override
public String getDesc() {
return super.getDesc() + "&&" + drink.getDesc();
}
}
具體裝飾角色:
package com.charon.decorator;
/**
* @className: Milk
* @description:
* @author: charon
* @create: 2022-03-19 23:17
*/
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDesc(" 牛奶 ");
setPrice(2.0f);
}
}
package com.charon.decorator;
/**
* @className: Soy
* @description:
* @author: charon
* @create: 2022-03-19 23:18
*/
public class Soy extends Decorator{
public Soy(Drink drink) {
super(drink);
setDesc(" 豆漿 ");
setPrice(1.5f);
}
}
測驗:
package com.charon.decorator;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-03-19 23:19
*/
public class Client {
public static void main(String[] args) {
// 點一份美式咖啡
Drink longBlack = new LongBlack();
System.out.println(longBlack.getDesc() + " 費用: " + longBlack.cost());
// 添加一點牛奶
longBlack = new Milk(longBlack);
System.out.println(" 添加了:" + longBlack.getDesc() + " 費用:" + longBlack.cost());
// 再添加一點豆漿
longBlack = new Soy(longBlack);
System.out.println(" 添加了:" + longBlack.getDesc() + " 費用:" + longBlack.cost());
}
}
列印:
美式咖啡 費用: 5.0
添加了: 牛奶 && 美式咖啡 費用:7.0
添加了: 豆漿 && 牛奶 && 美式咖啡 費用:8.5
裝飾器模式以對客戶端透明的方式擴展物件的功能,是繼承關系的一個替代方案,
裝飾器模式的優點:
- 裝飾器模式是對繼承的有利補充,比繼承靈活,在不改變原有物件的情況下,動態的給一個物件擴展功能,即插即用
- 通過使用不同裝飾類及這些裝飾類的排列組合,可以實作不同的效果
- 裝飾器模式完全遵循“開閉原則”
裝飾器模式的缺點:
- 裝飾器模式會增加許多子類,過度使用會增加程式的復雜性
裝飾器模式的應用場景
下面介紹其適用的應用場景,裝飾器模式通常在以下幾種情況使用,
- 當需要給一個現有類添加附加職責,而又不能采用生成子類的方法進行擴充時,例如,該類被隱藏或者該類是終極類或者采用繼承方式會產生大量的子類,
- 當需要通過對現有的一組基本功能進行排列組合而產生非常多的功能時,采用繼承關系很難實作,而采用裝飾器模式卻很好實作,
- 當物件的功能要求可以動態地添加,也可以再動態地撤銷,
裝飾器模式與橋接模式的區別
第一次看裝飾器模式,總感覺用橋接模式也能實作出來,但是其實兩者還是有區別的,
裝飾器模式的辦法就是把每個子類中比基類多出來的行為放到單獨的類里面,這樣當這些描述額外行為的物件被封裝到基類物件里面時,就得到了所需要的子類物件,將這些描述額外行為的類,排列組合可以造出很多的功能組合來,如果用靜態繼承的辦法創建這些組合出來的類所涉及到作業量很大,以致實際上很難做到,裝飾器模式要求所有的這些“額外行為類”具有相同的介面,
橋接模式的解決辦法則又有所不同,橋接模式把原來的兩個基類的實作化細節抽出來,再建造一個實作化的等級結構中,然后再把原有的基類改造成一個抽象化等級結構,橋接模式中抽象化角色的子類不能像裝飾器模式那樣嵌套,橋接模式卻可以連續使用,換言之,一個橋接模式的實作化角色可以成為下一步橋接模式的抽象化角色,
裝飾器模式與配接器模式的區別
裝飾器與配接器都有一個別名叫做 包裝模式(Wrapper),它們看似都是起到包裝一個類或物件的作用,但是使用它們的目的很不一樣,配接器模式的意義是要將一個介面轉變成另一個介面,它的目的是通過改變介面來達到重復使用的目的,
而裝飾器模式不是要改變被裝飾物件的介面,而是恰恰要保持原有的介面,但是增強原有物件的功能,或者改變原有物件的處理方式而提升性能,所以這兩個模式設計的目的是不同的,
在jdk中,InputStreamReader是一個配接器,因為它把InputStream的API轉換成Reader的API,InputStream是被適配的類,而 Reader是適配的目標類,InputStreamReader做為配接器類把InputStream類的一個實體包裝起來,從而能夠把InputStream的API,
而BufferReader是一個裝飾器類,因為它實作Reader,并且包裝了一個Reader,一些對流處理器可以對另一些流處理器起到裝飾作用,形成新的、具有改善功能得流處理器,類似地,BufferedInputStream、OutputStream、Writer 各自都是它們自己的裝飾類,LineNumberReader、FilterReader和 PushbackReader均是Reader的裝飾類,因為它們自己是Reader類,而且包裝其他的Reader類,CharArrayReader、FileReader、PipedReader和StringReader類不是裝飾類,因為它們包裝的是字符數值組、File、PipedWriter和String類,它們應該被看做字符數值組、File、PipedWriter 和String類的配接器類,
本文著作權歸Charon和博客園共有,原創文章,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利,轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/445596.html
標籤:其他
上一篇:創建型:三. 原型模式
