緣起
假如我們需要為遊戲中開發一種坦克,除不同型号的坦克外,我們還希望在不同場合中為其增加以下一種或多種功能:比如紅外線夜視功能,水陸兩栖功能,衛星定位功能……等等。
//抽象坦克
public abstract class Tank{
public abstract Shot();
public abstract Run();
}
//各種型号
public class T50:Tank{...}
public class T75:Tank{...}
public class T90:Tank{...}
//各種不同功能的組合
public class T50A:T50,IA{...}
public class T50B:T50,IB{...}
public class T50AB:T50,IA,IB{...}
...随着功能的擴充,子類數量急劇膨脹,子類複子類,子類何其多?
動機(Motivation)
上述問題的根源在于我們過度地使用了繼承來擴充對象的功能。由于繼承為類型引入的靜态特質,使得這種擴充方式缺乏靈活性;并且随着子類的增多(擴充功能的增多),各種子類的組合(擴充功能的組合)會導緻更多子類的膨脹(多繼承)。
如何使“對象功能的擴充”能夠根據需要來動态地實作?同時避免“擴充功能的增多”帶來的子類膨脹問題,進而使得任何“功能擴充變化”所導緻的影響降為最低?
對強類型語言來說,所謂“靜态特質”在這裡指在編譯時就确定了。一個功能就需要一個類型。動态則指綁定發生在運作時态或者運作時實作。
意圖(Intent)
動态地給一個對象增加一些額外的職責,就增加功能而言,Decorator模式比生成子類更為靈活。
——《設計模式》GoF
代碼示例:
public abstract class Tank
{
abstract void Shot();
abstract void Run();
}
public abstract Decorator:Tank //這實際是接口繼承,不是類繼承,表示在行為上可以作為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();
}
}
//裝飾類
public class DecoratorA:Decorator
{
public DecoratorA(Tank tank):base(tank)
{
}
public override void Shot()
{
//Do some extension 功能擴充...
base.Shot();
}
public override void Run()
{
//do some extension 功能擴充...
base.Run();
}
}
//裝飾類
public class DecoratorB:Decorator
{
public DecoratorB(Tank tank):base(tank)
{
}
public override void Shot()
{
//Do some extension 功能擴充...
base.Shot();
}
public override void Run()
{
//do some extension 功能擴充...
base.Run();
}
}
public class T50:Tank
{
public override void shot()
{
base.Shot();
}
public override void Run()
{
base.Run();
}
}
class App
{
public static void Main()
{
Tank tank = new T50();
Decoratora da = new DecoratorA(tank);
DecoratorB db = new DecoratorB(da);
//如此可以一直擴充下去...
//這樣在添加一個功能時隻需要添加一個裝飾類,實作了動态擴充
}
}
Decorator模式的要點
1)通過采用組合而非繼承的手法,Decorator實作了在運作時動态地擴充對象功能的能力,而且可以根據需要擴充多個功能,避免了單獨使用繼承帶來的“靈活性差”和“多子類衍生”問題。
2)Component類在Decorator模式中充當抽象接口的角色,不應該去實作具體的行為。而且Decorator類對于Component類應該透明——換言之,Component類無需知道Decorator類,Decorator類是從外部來擴充Component類的功能。
3)Decorator類在接口上表現為is-a Component的繼承關系,即Decorator類繼承了Component類所具有的接口。但在實作上又表現為has-a Component的組合關系,即Decorator類又使用了另外一個Component類。我們可以使用一外或者多個Decorator對象來“裝飾”一個Component對象,且裝飾後的對象仍然是一個Component對象。
4)Decorator模式并非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在于解決“主體類在多個方向上的擴充功能”——這就是“裝飾”的含義。裝飾是什麼?擴充而已。
.NET架構中的Decorator模式
以Stream類為例,它實際上是一個抽象類,而FileStream和NetworkStream等是繼承自它的實體類。而BufferedStream 和CryptoStream則為裝飾類。用法舉例如下:
class App
{
public static void Main()
{
MemoryStream ms = new MemoryStream(new byte[]{100,203,456,777,921,345});
BufferedStream buff = new BufferedStream(ms);//擴充了緩沖功能
CryptoStream crypto = new CryptoStream(buff);//擴充了緩沖和加密兩項功能
}
}