裝飾模式動態將責任附加在物件上,若要擴展功能上,裝飾者提供比繼承更有彈性的替代方案。
拿生活來說,我買了一棟三房兩廳兩衛沒裝潢的房子。由於房子過於老舊,我想要把一間房間裝潢成書房、一間裝潢成嬰兒房、最後一間裝潢成主臥。對於整體結構來說,房子不需要因為我要裝潢,而改變整體結構。只需要在既有的結構上,加上要裝潢的材料即可。又或是有一張相片,我想給相片加上項匡,而它本質還是一個相片。
而在系統上,通常要擴充功能,我們會用繼承的方式。但由於繼承耦合度高,且隨著擴充功能增加,子類別會越多,不易於維護。若使用組合關係的話,則可保持原本結構,且同時達到擴充的目的,這則是裝飾者模式的目標。
成員 | 功用 |
---|---|
Component | 定義一個物件的介面,可以給物件動態增加職責。 |
ConcreteComponent | 定義一個物件,可以給這個物件增加一些職責。(將ConcreteDecorator裝飾到他身上) |
Decorator | 維持一個指向Component物件的指標,並定義一個與Component介面一致的介面(可以直接實現Component介面)。 |
ConcreteDecorator | 元件新增的職責 |
註:這邊的職責,個人覺得用功能會比較好理解一點。
可以看到上面的UML圖,先將Component介面定義出來,分別由被裝飾者ConcreteComponent以及裝飾者Decorator去實作。接著Decorator再由其他要裝飾的方法去繼承,這樣就可以不更動原本的程式碼,也能擴充新的功能了。
知道了Decorator Pattern的四個成員後,現在我們要來一一實作每一個成員。我們用變形金剛來舉例:假設變形金剛裡的柯博文,他戰鬥時只有拳頭。而我們為了增強他的戰鬥能力,所以給他加了槍砲及劍的武器在他身上。首先要先將被裝飾者柯博文(ConcreteComponent)以及裝飾者武器(Decorator)的介面(Component)建立起來
interface Transformers {
public void startUp();
}
這個介面定義一個startUp()的方法供大家實作。接下來就是把柯博文給建立起來。
class OptimusPrime implements Transformers {
public OptimusPrime() {
System.out.println("I am Optimus Prime!! ");
}
public void startUp(){
System.out.println("Autobot !! Let's Go!!");
}
}
這樣柯博文成功的實作了Transformers內的方法。然後我們需要建立一個Weapon的Decorator。
class Weapon implements Transformers {
Transformers tf;
public Weapon(Transformers tf) {
this.tf = tf;
}
public void startUp() {
tf.startUp();
}
}
再來就是建立武器庫了。這裡建立槍和劍的類別。其中增加一個新的方法setWeapon(),這就是為了強化柯博文而新增的功能。
class Sword extends Weapon {
public Sword(Transformers tf) {
super(tf);
}
public void startUp() {
setWeapon();
tf.startUp();
}
public void setWeapon(){
System.out.println("I get a Sword!");
}
}
class Gun extends Weapon {
public Gun(Transformers tf) {
super(tf);
}
public void startUp() {
setWeapon();
tf.startUp();
}
public void setWeapon(){
System.out.println("I get a Gun!");
}
}
最後來執行
public class Autobot {
public static void main(String args[]) {
Transformers op1 = new OptimusPrime();
op1.startUp();
System.out.println("-----");
Transformers op2 = new Sword(op1);
op2.startUp();
System.out.println("-----");
Transformers op3 = new Gun(op1);
op3.startUp();
}
}
output
I am Optimus Prime!!
Autobot !! Let's Go!!
-----
I get a Sword!
Autobot !! Let's Go!!
-----
I get a Gun!
Autobot !! Let's Go!!
就程式碼來看,新增武器,並不需要更動到柯博文的類別。若是未來要增加飛行裝置,或是恐龍坐騎,都只需要新增一個類別就可以,不需要異動到原始的程式碼。若把程式碼的角色套入剛剛的UML,就會如下圖所示。
動態地給一個物件新增一些額外的職責
Component:定義裝飾者與被裝飾者要實作的介面。
ConcreteComponent:被裝飾者
Decorator:裝飾者的父類別。
ConcreteDecorator:裝飾者。
優點
1. 裝飾類以及被裝飾類可獨立發展,不會互相耦合。
2. 相較於繼承,整體擴充彈性較大。
缺點
子類別眾多,若使用不當會是系統變得相當複雜。
1. 需要在不影響元件物件的情況下,以動態、透明的方式給物件新增職責。
2. 當不能採用繼承的方式對系統進行擴充或者採用繼承不利於系統擴充套件和維護時可以考慮使用裝飾類。
装饰模式(装饰设计模式)详解
装饰器模式
裝飾模式(Decorator Pattern)