微信關注公眾號 JavaStorm 獲取最新內容。
裝飾器模式(Decorator),動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾器模式比生成子類更為靈活;它允許向一個現有的對象添加新的功能,同時又不改變其結構。裝飾器模式屬於結構型模式。
UML 類圖
- Component:接口,定義一個抽象接口裝飾對象與真實對象具有相同的接口,以便裝飾器動態的添加職責。
- ConcreteComponent: 接口的具體對象。
- Decorator:裝飾類,繼承了 Component , 從外類來拓展 Component 的功能 並且持有一個 Component 的引用,通過構造器實例化,從而實現對真實對象的職責裝飾增強。
- ConcreteDecorator:具體裝飾類,用於給實際對象添加職責。
使用場景
現在有一個場景:煎餅果子,科技園上班族早上去買煎餅果子(Pancake),有的人要加雞蛋 (Egg)、有的人加火腿 (Ham)、有的人加生菜 (Lettuce)。有的土豪煎餅果子來一套全都要。現在我們來定義煎餅烹飪實現。(ps:留一個功能讀者自己實現:不同的套餐價格是不一樣的,如何計算出不同煎餅果子的價格?有興趣的讀者可以留言或者微信公眾號后台留言)。
代碼實現
代碼可以左右滑動
- 先定義煎餅接口也就是我們的被裝飾類,以及烹飪的方法 。
package com.zero.headfirst.decorator;
public interface Pancake {
/**
* 烹飪方法
*/
void cook();
}
- 定義一個乞丐版煎餅,被裝飾對象。
package com.zero.headfirst.decorator;
/**
* 被裝飾對象:定義最基本的乞丐版煎餅,啥都沒加
*/
public class BeggarPancake implements Pancake {
@Override
public void cook() {
System.out.println("乞丐版基本煎餅");
}
}
- 定義抽象裝飾類 煎餅果子裝飾器 PancakeDecorator:抽象裝飾器角色,實現煎餅接口(被裝飾器接口),持有被裝飾器的引用 (pancake)將烹飪行為轉發具體的裝飾器。
package com.zero.headfirst.decorator;
/**
* 抽象裝飾器角色,實現煎餅接口(被裝飾器接口),持有被裝飾器的引用將烹飪行為轉發具體的裝飾器。
*/
public abstract class PancakeDecorator implements Pancake {
private Pancake pancake;
public PancakeDecorator(Pancake pancake) {
this.pancake = pancake;
}
@Override
public void cook() {
if (pancake != null) {
pancake.cook();
}
}
}
- 各種具體裝飾類對乞丐版煎餅進行不等級別的土豪加工。首先繼承 抽象出來的 PancakeDecorator ,重寫 cook 方法,實現加工。
EggDecorator 雞蛋裝飾器:繼承 PancakeDecorator,重寫 cook 方法。動態添加雞蛋,然后調用pancake 的cook。
package com.zero.headfirst.decorator;
/**
* 雞蛋裝飾器:覆蓋cook方法,加入自身的實現,並且調用父類的cook方法,也就是構造函數中
* EggDecorator(Pancake pancake),這里傳入的pancake的cook操作
*/
public class EggDecorator extends PancakeDecorator {
public EggDecorator(Pancake pancake) {
super(pancake);
}
@Override
public void cook() {
System.out.println("加一個雞蛋;");
super.cook();
}
}
火腿裝飾器: HamDecorator
package com.zero.headfirst.decorator;
/**
* 火腿裝飾器
*/
public class HamDecorator extends PancakeDecorator {
public HamDecorator(Pancake pancake) {
super(pancake);
}
@Override
public void cook() {
System.out.println("加一個火腿;");
super.cook();
}
}
生菜裝飾器
package com.zero.headfirst.decorator;
/**
* 生菜裝飾器
*/
public class LettuceDecorator extends PancakeDecorator {
public LettuceDecorator(Pancake pancake) {
super(pancake);
}
@Override
public void cook() {
System.out.println("加生菜;");
super.cook();
}
}
- 定義一個煎餅果子攤位。
package com.zero.headfirst.decorator;
/**
* 煎餅果子店
*/
public class PancakeShop {
public static void main(String[] args) {
System.out.println("========土豪來了,全都加上。======");
BeggarPancake beggarPancake = new BeggarPancake();
EggDecorator eggDecorator = new EggDecorator(beggarPancake);
HamDecorator hamAndEggDecorator = new HamDecorator(eggDecorator);
LettuceDecorator lettuceAndHamAndEggDecorator = new LettuceDecorator(hamAndEggDecorator);
lettuceAndHamAndEggDecorator.cook();
System.out.println("========苦逼碼農來了,只要雞蛋補補腦。=====");
BeggarPancake beggarPancake1 = new BeggarPancake();
EggDecorator eggDecorator1 = new EggDecorator(beggarPancake1);
eggDecorator1.cook();
}
}
- 運行結果
========土豪來了,全都加上。======
加生菜;
加一個火腿;
加一個雞蛋;
乞丐版基本煎餅
========苦逼碼農來了,只要雞蛋補補腦。=====
加一個雞蛋;
乞丐版基本煎餅
總結
真實世界的裝飾: Java I/O。
注意事項與要點
- 抽象裝飾器與具體被裝飾對象實現同一個接口。
- 抽象裝飾器持有被裝飾器接口對象,以便請求傳遞。
- 具體裝飾器需要重寫抽象裝飾器的方法並引用super進行條用,轉發請求。
適用場景
-
拓展一個類的功能。
-
動態的添加與撤銷職責。
優點
- 裝飾類與被裝飾類只關心自己的核心邏輯,實現了解耦。
- 方便動態拓展,開閉原則。且比繼承靈活。
缺點
- 如果功能拓展太多,將產生大量的類。
- 多層裝飾會變得復雜。
以上代碼可參考我的 GitHub : https://github.com/UniqueDong/zero-design-stu。headfirst包下。歡迎關注公眾號: JavaStorm 獲取最新文章,也可在后台留言提出意見。收藏與關注是最大的鼓勵。