設計模式(三)裝飾者模式
阿新 • • 發佈:2018-12-16
參考:
1. 概念解析
裝飾者模式:在不改變原類檔案和繼承的情況下,動態的拓展一個物件的功能,通過建立一個包裝物件,也就是裝飾來包裹真實的物件。 特點:
- 裝飾物件和真實物件有相同的介面,這樣客戶端物件就能用同樣的方式和裝飾物件互動(與真實物件互動也一樣的方式)。
- 裝飾物件包含一個真實物件的引用,這個引用就是為了下面的這一點,呼叫真實物件的方法。
- 裝飾物件接受所有來自客戶端的請求,把這些請求轉發給真實物件。
- 裝飾物件可以再轉發請求時,新增一些自己的附加功能,所以在執行時,可以不修改原真實物件,達到拓展功能的意圖。通常通過繼承來實現對指定類的功能拓展。
截個圖放在這裡,反正你也看不懂,先跳過往下看。
2. 應用場景
- 需要拓展一個類的功能,或者給一個類新增附加職責
- 需要動態的給一個物件新增功能,並且可以動態的撤銷這些功能。
- 需要增加由一些基本的功能排列組合而產生的大量的功能。
- 不能採用生成子類的方法進行擴充功能時,或者是類的定義被隱藏,或者不能用於生成子類。 java.io這個包就是典型的裝飾者模式設計的。 再詳細一點的解釋: BufferedInputStream及LineNumberInputStream都擴充套件自 FilterInputStream,而FilterInputStream是一個抽象的裝飾類。
不過裝飾者模式也有個缺點,就是會建立很多的小類,那麼在使用這些包裝類的時候可能會造成一定的困擾。
3. 模式組成
裝飾者由4個元素組成
- 抽象元件(Component),beverage,一般是一個抽象類或者介面,用來規範準備接受附加責任的物件
- 具體元件(ConcreteComponent),houseblend,實現了抽象元件的類,定義一個將要接收附加責任的類。
- 抽象裝飾者(Decorator),condimentdecorator,持有一個元件物件的例項,並實現一個與抽象元件一致的介面
- 具體裝飾者,mocha。新增附加責任。
4. 程式碼實現
來杯2杯咖啡,一個原味,一個加摩卡。。
- 基礎的飲料抽象元件:
public abstract class Beverage {
public String description = "xxx";
//列印一些文字資訊
public String getDescription() {
return description;
}
public abstract double cost();
}
- 具體的實現,在計算中直接返回咖啡的價格。
public class Espresso extends Beverage {
public Espresso(){
description = ".Espresso.";
}
@Override
public double cost() {
return 1.99;
}
}
- 為了實現有摩卡的咖啡,我們不能修改咖啡的部分,而是把摩卡當成元件插入。那麼就來一個裝飾者。
public abstract class CondimentDecorator extends Beverage{
@Override
public abstract String getDescription();
}
- 實現這個裝飾者,並加上一些額外的功能。
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + "...mocha";
}
@Override
public double cost() {
return 1.89 + beverage.cost();
}
}
- 測試
@Test
public void test(){
Beverage b = new Espresso();
System.out.println(b.getDescription() + " " + b.cost());
Beverage b2 = new Espresso();
b2 = new Mocha(b2);
System.out.println(b2.getDescription() + " " + b2.cost());
}
列印:
.Espresso. 1.99
.Espresso....mocha 3.88
看到加了mocha裝飾者,並沒有修改原咖啡類。
另外一個例子,新建一個自己的java io裝飾類,把讀取的檔案中所有的大寫字母變成小寫的。
public class LcInputStream extends FileInputStream {
public LcInputStream(String name) throws FileNotFoundException {
super(name);
}
@Override
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char) c));
}
@Override
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for (int i = offset; i < offset + result; i++) {
b[i] = (byte) Character.toLowerCase((char) b[i]);
}
return result;
}
}
測試程式碼:
try {
int c;
// 讀取檔案的地址
InputStream in = new LcInputStream("/Users/xx/Downloads/fee.html");
in.read();
while ((c = in.read()) >= 0) {
System.out.print((char) c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
列印:讀取的是html檔案,有大小寫的,得到的結果都是小寫
5. 優缺點
優點,靈活,通過組合實現很多不同的行為。 缺點,太靈活,增加了複雜性,會產生很多小類。
6. 總結
有一點模糊,不是很理解。需要繼續學習。
7. headfirst讀書分享
- 類應該對拓展開放,對修改關閉(開放-關閉原則)