意圖(Intent)
動態地給一個對象增加一些額外的職責。就增加功
能而言,Decorator模式比生成子類更爲靈活。
——《設計模式》GoF
結構圖(stucture)
前面我們用bridgh 模式在不同緯度進行擴展
子類復子類,子類何其多
假如我們需要爲遊戲中開發一種坦克,除了各種不同型號
的坦克外,我們還希望在不同場合中爲其增加以下一種或
多種功能:比如紅外線夜視功能,比如水陸兩棲功能,比
如衛星定位功能等等。
// 抽象坦克
public abstract Shot();
public abstract Run();
}
//各種型號
public class T50: Tank {……}
public class T75: Tank {……}
public class T90: Tank {……}
//各種不同功能的組合
public class T50A: T50, IA {…}//IA爲一個接口
public class T50B: T50, IB {…}
public class T50C: T50, IC {…}
public class T50AB: T50, IA, IB {…}//要擴展來兩個功能
public class T50BC: T50, IB, IC {…}
public class T50ABC: T50, IA, IB, IC{...}
。。。
我們在加一個功能ID
就會變成I50AD,I50BD,I50CD,I50ABD...
子類將會膨脹。擴展一個功能就要對子類進行擴展膨脹。
動機(Motivation)
上述描述的問題根源在於我們“過度地使用了繼承來擴展對
象的功能”,由於繼承爲類型引入的靜態特質,使得這種擴
展方式缺乏靈活性;並且隨着子類的增多(擴展功能的增
多),各種子類的組合(擴展功能的組合)會導致更多子
類的膨脹(多繼承)。
如何使“對象功能的擴展”能夠根據需要來動態地實現--功能的綁定都是在運行時?同時
避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何
“功能擴展變化”所導致的影響將爲最低?
用接口來擴展功能
{
abstract void Shot();
abstract void Run();
}
public class T50:Tank
{
public override void Shot()
{}
public override void Run()
{}
}
public class T75:Tank
{
public override void Shot()
{}
public override void Run()
{}
}
public class T90:Tank
{
public override void Shot()
{}
public override void Run()
{}
}
public interface IA
{
void ShotA();
void RunA();
}
public class T50:Tank,IA
{ void IA.ShotA();
{
}
void IA.RunA();
public override void Shot()
{
ShotA();//功能擴展
// do
base.Shot();
}
public override void Run()
{
RunA();
//do
base.Run();
}
用了 decorator 模式
{
public abstract void Shot();
public abstract void Run();
}
public class T50:Tank
{
public override void Shot()
{}
public override void Run()
{
Console.WriteLine("開戰了");
}
}
public abstract class Decorator :Tank//接口繼承--抽象類可以表示接口
{
private Tank _tank; //Has A 對象組合
public Decorator (Tank tank)
{
_tank=tank ;
}
public override void Shot()
{
_tank.Shot();
}
public override void Run()
{
_tank.Run();
}
}
public class DecoratorA :Decorator
{
public DecoratorA (Tank tank):base(tank)
{
}
public override void Shot()
{ // 紅外功能擴展
// do shot
Console.WriteLine("紅外功能擴展");
base.Shot();
}
public override void Run()
{
// 紅外功能擴展
// do run
Console.WriteLine("紅外功能擴展");
base.Run();
}
}
public class DecoratorB :Decorator
{
public DecoratorB (Tank tank):base(tank)
{
}
public override void Shot()
{ // 水路兩棲擴展
// do shot
Console.WriteLine("水路兩棲擴展");
base.Shot();
}
public override void Run()
{
// 水路兩棲功能擴展
// do run
Console.WriteLine("水路兩棲擴展");
base.Run();
}
}
public class DecoratorC :Decorator
{
public DecoratorC (Tank tank):base(tank)
{
}
public override void Shot()
{ // 衛星能擴展
// do shot
Console.WriteLine("衛星能擴展");
base.Shot();
}
public override void Run()
{
// 衛星功能擴展
// do run
Console.WriteLine("衛星能擴展");
base.Run();
}
}
public class App
{
public static void Main()
{
Tank tank=new T50();
DecoratorA da=new DecoratorA(tank);//紅外 繼承Tank就是能自己 裝飾
DecoratorB db=new DecoratorB(da);//紅外,兩棲
DecoratorC dc=new DecoratorC(db);//紅外,兩棲,衛星。
dc.Run();
}
}
• 通過採用組合、而非繼承的手法, 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模式
應用的要點在於解決“主體類在多個方向上的擴展功能”——是爲“裝飾”
的含義。