Gof定義
動態地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更爲靈活。
首先來看一個小例子,假如我們需要給遊戲開發一種坦克,除了各種不同型號的坦克外,還希望在不同的場合來給坦克增加以下的一種或多種功能:比如紅外線夜視功能、水陸兩棲功能、GPS定位功能等。通常做法如下:
/// <summary> /// 抽象坦克 /// </summary> public abstract class Tank { public abstract void Shot(); public abstract void Run(); } //型號 public class T50 : Tank { } public class T60 : Tank { } public class T70 : Tank { } //在不同場合下的各種功能都抽象爲接口, //如果某種型號的坦克需要某種功能就繼承該功能接口 //下面的IA IB IC 爲功能A B C的接口 /// <summary> /// 有A功能的T50型號 /// </summary> public class T50A : T50, IA { } /// <summary> /// 有A B兩種功能的T60型號 /// </summary> public class T60AB : T60, IA, IB { } /// <summary> /// 有A B C三種功能的T70型號 /// </summary> public class T70ABC : T70, IA, IB, IC { }
動機
上面描述的問題的根源在於我們“過多地使用了繼承來擴展對象的功能”,由於繼承爲賴幸引入了靜態特質,使得這種擴展方式缺乏靈活性,並且隨着子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹(多繼承)。那麼如何使“對象功能的擴展”能夠根據需要來動態地實現,同時避免“擴展功能的增多”帶來的子類膨脹問題,從而使得任何“功能的變化”所導致的影響減爲最低呢?這就需要用到裝飾模式(Decorator)。下面先來看下裝飾模式的結構圖:
Component:對應上面例子中的Tank。
ConcreteComponent:對應坦克的型號 T50 T60 T70等。
DeCorator ConcreteDecoratorA ConcreteDecoratorB 這三個類就是接下來要實現了。
public abstract class Decorator : Tank { private Tank _tank; public Decorator(Tank tank) { _tank = tank; } public override void Shot() { _tank.Shot(); } public override void Run() { _tank.Run(); } } public class ConcreteDecoratorA : Decorator { public ConcreteDecoratorA(Tank tank):base(tank){} public override void Shot() { //擴展紅外功能 base.Shot(); } public override void Run() { base.Run(); } } public class ConcreteDecoratorB : Decorator { public ConcreteDecoratorB(Tank tank):base(tank){} public override void Shot() { //擴展兩棲功能 base.Shot(); } public override void Run() { base.Run(); } } public class ConcreteDecoratorC : Decorator { public ConcreteDecoratorC(Tank tank) : base(tank) { } public override void Shot() { //擴展定位功能 base.Shot(); } public override void Run() { base.Run(); } }
客戶端的調用
public class App { static void Main() { Tank tank = new T50(); //擁有紅外一種功能 ConcreteDecoratorA da = new ConcreteDecoratorA(tank); //擁有紅外和兩棲兩種功能 ConcreteDecoratorB db = new ConcreteDecoratorB(da); //擁有紅外、兩棲和定位三種功能 ConcreteDecoratorC dc = new ConcreteDecoratorC(db); //需要什麼功能可以在此隨意組合,如果要擴展一種功能,只需要 //添加一個具體的功能的類繼承Decorator即可 } }
Decorator模式的幾個要點
通過採用組合、而非繼承的手法, Decorator模式實現了在運行時動態地擴展對象功能的能力,而且可以根據需要擴展多個功能。避免了單獨使用繼承帶來的“靈活性差”和“多子類衍生問題”。
Component類在Decorator模式中充當抽象接口的角色,不應該去實現具體的行爲。而且Decorator類對於Component類應該透明——換言之Component類無需知道Decorator類,Decorator類是從外部來擴 展Component類的功能。
Decorator類在接口上表現爲is-a Component的繼承關係,即Decorator類繼承了Component類所具有的接口。但在實現上又表現爲has-a Component的組合關係,即Decorator類又使用了另外一個Component類。我們可以使用一個或者多個Decorator對象來“裝飾”一個Component對象,且裝飾後的對象仍然是一個Component對象。
Decorator模式並非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在於解決“主體類在多個方向上的擴展功能”——是爲“裝飾”的含義。