在某些情況下我們可能會“過度地使用繼承來擴充項物件的功能”,由於繼承為類型引入的靜態(編譯時間)特質,使得這種擴充方式缺乏靈活性;並且隨著子類的增多(擴充功能的增多),各種子類的組合(擴充功能的組合)會導致更多子類的膨脹(多繼承)。
如何使“對象功能的擴充”能夠根據需要來動態(運行時)地實現?同時避免“擴充功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴充變化”所導致的影響將為最低?
意圖(Intent)
動態地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比產生子類更為靈活。
public abstract class Tank
{
public abstract void Shot();
public abstract void Run();
}
public abstract class Decorator : Tank //介面繼承
{
private Tank tank;//Has-A 對象組合
public Decorator(Tank tank)
{
this.tank = tank;
}
public override void Shot()
{
tank.Shot();
}
public override void Run()
{
tank.Run();
}
}
/// <summary>
/// 裝飾類A
/// </summary>
public class DecoratorA : Decorator
{
public DecoratorA(Tank tank)
: base(tank)
{
}
public override void Shot()
{
//紅外功能擴充
//Do Shot...
base.Shot();
}
public override void Run()
{
//紅外功能擴充
//Do Run...
base.Run();
}
}
/// <summary>
/// 裝飾類B
/// </summary>
public class DecoratorB : Decorator
{
public DecoratorB(Tank tank)
: base(tank)
{
}
public override void Shot()
{
//水陸兩棲功能擴充
//Do Shot...
base.Shot();
}
public override void Run()
{
//水陸兩棲功能擴充
//Do Run...
base.Run();
}
}
/// <summary>
/// 裝飾類C
/// </summary>
public class DecoratorC : Decorator
{
public DecoratorC(Tank tank)
: base(tank)
{
}
public override void Shot()
{
//衛星定位功能擴充
//Do Shot...
base.Shot();
}
public override void Run()
{
//衛星定位功能擴充
//Do Run...
base.Run();
}
}
class App
{
public static void Main()
{
Tank tank = new T50();
DecoratorA da = new DecoratorA(tank);//紅外功能擴充
DecoratorA db = new DecoratorA(da);//水陸兩棲功能擴充
DecoratorA dc = new DecoratorA(db);//衛星定位功能擴充
}
}
要點:
通過採用組合而非繼承的手法, Decorator模式實現了在運行時動態擴充項物件功能的能力,而且可以根據需要擴充多個功能。避免了使用繼承帶來的“靈活性差”和“多子類衍生問題”。
Decorator類在介面上表現為is-a Component的繼承關係,即Decorator類繼承了Component類所具有的介面。但在實現上又表現為has-a Component的組合關係,即Decorator類又使用了另外一個Component類。
Decorator模式並非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在於解決“主體類在多個方向上的擴充功能”——是為“裝飾”的含義。
適用性
在以下情況下應當使用裝飾模式:
1.需要擴充一個類的功能,或給一個類增加附加責任。
2.需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
3.需要增加由一些準系統的排列組合而產生的非常大量的功能,從而使繼承關係變得不現實。