天天看點

設計模式學習筆記(十)Decorator裝飾(結構型模式)

 緣起

假如我們需要為遊戲中開發一種坦克,除不同型号的坦克外,我們還希望在不同場合中為其增加以下一種或多種功能:比如紅外線夜視功能,水陸兩栖功能,衛星定位功能……等等。

//抽象坦克

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);//擴充了緩沖和加密兩項功能

 }

}

繼續閱讀