意圖
動態地給一個對象添加一些額外的職責,就增加功能來說,Decorator模式相比生成子類更爲靈活
別名:包裝器Wrapper
裝飾器模式的誕生
有時我們希望給某個對象而不是整個類添加一些功能,例如:一個圖形用戶界面工具箱允許你對任意一個用戶界面組件添加一些特性,例如邊框。
使用繼承機制是添加功能的一種有效途徑,但這種方法不夠靈活,因爲邊框的選擇是靜態的,用戶不能控制對組件加邊框的方式和時機。一種較爲靈活的方式是將組件嵌入另一個對象中,由這個對象添加邊框,我們稱這個嵌入的對象爲裝飾。
說人話就是:
【產品】:開發小哥,我這裏有一份代碼,你需要調用它的功能並補充公司的額外功能但是不能動它本身的內容,能做到嗎?
【開發】:哈?怎麼可能!怎麼可能不改代碼還要他功能,還得有額外的功能?難道讓我寫成這樣的?
void demo () {
// 調用原有代碼功能
// do();
// 調用公司功能
// doMine();
}
【BOSS】:磕大頭!你不覺得這樣很low嗎?
【開發】:可是不這麼寫我咋改寫?
【BOSS】:你仔細看看原有的代碼,它們都實現了同樣的接口,這就是一個突破點
【開發】:我去研究研究…
HeadFirst 核心代碼
於是乎,我們開啓了關於設計模式的經典書籍閱讀之旅
/**
* 裝飾器模式共同的接口
*/
public interface Component {
String getName();
double getSpend();
}
/**
* 原本的類
*/
public class ConcreteComponent implements Component {
private String name;
public ConcreteComponent(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public double getSpend() {
return 10;
}
}
/**
* 裝飾器類 注意它利用了組合的方式,同時注意函數實現的部分
*/
public class MilkDecorator implements Component {
Component coffe;
MilkDecorator(Component coffe) {
this.coffe = coffe;
}
@Override
public String getName() {
return coffe.getName() + ", 牛奶";
}
@Override
public double getSpend() {
return coffe.getSpend() + 2D;
}
}
裝飾器模式的設計思路:
- Component:定義一個對象接口,統一行爲及父級
- ConcreteComponent:定義一個對象,可以給這個對象添加一些職責(方法)
- Decorator(不必要):定義一個跟Component一致的接口,並約束需要實現的職責(方法)
- ConcreteDecorator:維持一個指向Component對象的指針,並實現Component接口,以此增強對象的行爲
簡單來說,
- 我們需要一個接口來統一父級,並制定一些必要的行爲
- 默認的實現類實現相關功能
- 如果需要額外的增加(裝飾),則實現接口並持有接口對象的指針,在實現接口方法時調用指針對象的方法並增強
如果看着有點模棱兩可,就看完本文後,訪問專題設計模式開源項目,裏面有具體的代碼示例,鏈接在最下面
遵循的設計原則
- 對擴展開放,對修改關閉:完美實現了不修改其代碼達到增強方法的目的
- 針對接口編程:裝飾器與其本身都有同樣的父級
什麼場景適合使用
在不影響其他對象的情況下,以動態,透明的方式給單個對象添加職責
Code/生活中的實際應用
Java 中最著名的裝飾者模式的應用,就是 java.io 包中的各種InputStream 、Reader和 Writer 的實現類了。如果我們要從文件中讀取文本內容,我們可以使用 FileReader(裝飾者) 和 FileInputStream(裝飾者);
但是如果我們想讓讀取的速度更快,那麼我們就需要 BufferedReader(裝飾者) ,這只需要改變一行代碼就能實現
最後
附上GOF一書中對於裝飾器模式的UML圖:
相關代碼鏈接
- 兼顧了《HeadFirst》以及《GOF》兩本經典數據中的案例
- 提供了友好的閱讀指導