【設計模式(九)】結構型模式之裝飾器模式
個人學習筆記分享,當前能力有限,請勿貶低,菜鳥互學,大佬繞道
如有勘誤,歡迎指出和討論,本文後期也會進行修正和補充
前言
中秋剛過沒多久,雖然我這種粗人對月餅無感,但是公司發的肯定得收的嘛
拿回家當零食吃算了,一個硬紙袋,開啟是一個盒子,盒子開啟時十多個小盒子,小盒子開啟是塑料包裝的月餅,撕開塑料包裝,終於能吃了
吃了一口,emmm就這?還沒小時候吃的冰糖五仁月餅好吃呢,弄這麼花裡胡哨
就一塊味道並不咋地的月餅,花裡胡哨包裝一層又一層,何必呢?還不如改善工藝做好吃點
說的再難聽點,這些精心包裝的月餅,跟普通袋裝的月餅,區別也就只是包裝吧(雖然大部分人送的是心意)
包裝對於月餅,就只是裝飾而已,差不多做法生產的月餅,經過一層又一層包裝,就能變成在店鋪裡形形色色的禮品月餅,本質上依然是月餅
而且理論上包裝可以無限層的套娃。。。。
我們早上去早餐店買煎餅,可以加雞蛋、生菜、肉鬆等等,從一個薄餅整成一個巨無霸,但說到底,仍然是個煎餅。
裝飾器模式(Decorator Pattern)允許向一個現有的物件新增新的功能,同時又不改變其結構。
這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
添加了新功能後是原物件的子類,根據里氏代換原則,新的物件也能夠被再次修飾,進而可以無限套娃
1.介紹
使用目的:向一個現有的物件新增新的功能,同時又不改變其結構
使用時機:在不想改變結構的情況下擴充套件類
解決問題:避免擴充套件時常規繼承方法,隨著擴充套件功能的增多,子類會很膨脹
實現方法:Component
類充當抽象角色,修飾類引用和繼承 Component 類,具體擴充套件類重寫父類方法,而修飾類也同樣可被擴充套件
應用例項:
- 買手抓餅的時候,我們並非只有做之前可以說加什麼,做完之後加肉鬆,或者加火腿什麼的都是可以的,但怎麼加都是手抓餅
- 化妝的時候,經常是塗塗抹抹好幾層,而且順序並不嚴格固定,也可以最後根據情況再塗幾層,怎麼好看怎麼來,但怎麼塗都是自己的臉
優點:
- 裝飾類和被裝飾類可以獨立發展,不會相互耦合
- 可以被多次裝飾,使用時自由度高
缺點:多層裝飾時思路比較複雜,需要清晰的理解業務
2.結構
通常包括4個角色
- 抽象元件角色(Component)
- 具體元件角色(ConcreteComponent):定義一個要被裝飾器裝飾的物件,即
Component
的具體實現 - 抽象裝飾器(Decorator): 維護對元件物件和其子類元件的引用,需要實現
Component
- 具體裝飾器角色(ConcreteDecorator):向元件新增新的職責
ConcreteComponent
負責Component
的具體實現Decorator
是一個虛擬類,需要實現Component
,並持有一個Component
物件ConcreteDecorator
作為裝飾器,負責給元件新增職責
ConcreteDecorator
作為Decorator
的子類,根據里氏代換原則,也可被ConcreteDecorator
持有並裝飾
說白了就是,我裝飾我自己
3.實現
-
定義抽象元件角色(
Component
)interface Component { String operate(); }
-
定義具體元件角色(
ConcreteComponent
),並實現Component
class ConcreteComponent implements Component { @Override public String operate() { return "原始物件"; } }
-
定義抽象裝飾器(
Decorator
),實現Component
abstract class Decorator implements Component { private Component component; public Decorator(Component component) { this.component = component; } @Override public String operate() { //呼叫被裝飾者的方法 return component.operate(); } }
-
定義具體裝飾器角色(
ConcreteDecorator
),繼承Component
class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } @Override public String operate() { return super.operate() + " 加上修飾器A"; } } class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } @Override public String operate() { return super.operate() + " 加上修飾器B"; } }
-
測試客戶端
public class DecoratorTest { public static void main(String[] args) { Component component = new ConcreteComponent(); Component componentA=new ConcreteDecoratorA(component); Component componentAB=new ConcreteDecoratorB(componentA); Component componentABA=new ConcreteDecoratorA(componentAB); Component componentBAB=new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteDecoratorB(new ConcreteComponent()))); System.out.println(componentABA.operate()); System.out.println(componentBAB.operate()); }
完整程式碼
package com.company.test.decorator;
interface Component {
String operate();
}
class ConcreteComponent implements Component {
@Override
public String operate() {
return "原始物件";
}
}
abstract class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public String operate() {
//呼叫被裝飾者的方法
return component.operate();
}
}
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public String operate() {
return super.operate() + " 加上修飾器A";
}
}
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public String operate() {
return super.operate() + " 加上修飾器B";
}
}
public class DecoratorTest {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component componentA=new ConcreteDecoratorA(component);
Component componentAB=new ConcreteDecoratorB(componentA);
Component componentABA=new ConcreteDecoratorA(componentAB);
Component componentBAB=new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteDecoratorB(new ConcreteComponent())));
System.out.println(componentABA.operate());
System.out.println(componentBAB.operate());
}
}
執行結果
4.小結
4.1.為何要使用裝飾模式
-
解耦
ConcreteComponent
和ConcreteDecorator
,從常見的繼承關係變為關聯關係,使他們可以獨立變化,只需要遵守共同的規則(Component
)即可否則只能使他們一層一層繼承,耦合度極高,不利於擴充套件
-
ConcreteComponent
和ConcreteDecorator
都算是Component
的實現,因此對於Component
適用的介面,ConcreteComponent
和ConcreteDecorator
都適用,這樣以來,客戶端使用和外部介面對接也會簡單很多因此外部程式碼只需要針對
Component
設計即可 -
可以自己裝飾自己,是獨一無二的優點,可以無限制的自由增加功能
4.2.存在的隱患
均為設計或者管理不當導致,也就是人為錯誤,可以憑藉開發者能力規避的東西
-
套娃次數太多,會導致邏輯混亂
比如server層之間的互相引用,很容易出現套娃,邏輯極其混亂
-
死迴圈
類似於遞迴的死迴圈爆棧
-
裝載順序報錯(先有雞還是先有蛋?)
比如兩個service(A和B)互相引用,那麼裝載A時需要先裝載B,裝載B時需要先裝載A,會直接報錯
後記
將相似的目標提取其共同點,從而可以進行部分一致性操作,而目標本身只需要關注自己的特點,將共同點交由介面或者父類處理
其實這也是多型和繼承的目的,所以從學習Java開始,我們其實就在按照這種思想設計程式,組合模式不過是其中一種方案而已
作者:Echo_Ye
WX:Echo_YeZ
Email :[email protected]
個人站點:在搭了在搭了。。。(右鍵 - 新建資料夾)