當我們在設計一個一個類繼承結構的時候,我們可能會存在以下的情況。首先有一種坦克的抽象類,那麼這個坦克會有很多中類型,那麼我們就派生這個坦克吧。比如說有德國虎式坦克,輕型坦克和工程坦克,那麼現在又有三種功能要擴展到這三種坦克上,如兩棲功能、夜視功能、雷達功能,可以把這三種功能設置為接口。按類繼承的設計方案,如果需要這三種坦克每種坦克都有相應的功能坦克,我們需要繼承Tank類,並實現相應功能的接口,那麼我們的擴展類就有9種,這還不包括功能的組合,那麼現在問題就出來了,這種類繼承的模式會導致子類無限制的增加,這是我們無法忍受的。那麼怎麼解決這個問題呢?我們就需要Decorator模式來解決。
Decorator模式的動機:
上述描述的問題根源在於我們“過度地使用了繼承來擴展對象的功能”,由於繼承為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;並且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹(多繼承)。如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將為最低?
Decorator模式的意圖:
動態地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更為靈活。
如何用代碼來實現Decorator模式:
首先我們有一個抽象基類Tank,裡面有兩個抽象方法Shot和Run
public abstract class Tank
...{
public abstract void Shot();
public abstract void Run();
}
我們有三個派生類來表明三種坦克的類型,並實現了Shot和Run方法
public class TigerTank : Tank
...{
public override void Shot()
...{
}
public override void Run()
...{
}
}
public class LightTank : Tank
...{
public override void Shot()
...{
}
public override void Run()
...{
}
}
public class ConsturctTank : Tank
...{
public override void Shot()
...{
}
public override void Run()
...{
}
}現在,我們按照往常的設計方法應該寫功能接口interface了,但是功能接口會帶來類的膨脹,這不是我們要的目的,那麼我們看下面的代碼式如何做的。
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();
}
這是裝飾接口的基類,他繼承與Tank類,這裡繼承的Tank類不是一個is a的關系,他就像一個接口,規定了Decorator類要實現的方法。_tank作為一個Tank的實例,是一個Has a的對象組合關系。Decroator實現的Shot和Run方法是調用私有成員變量_tank的Shot和Run方法。
下面就是兩棲功能的實現
public class Amphibian : Decorator
...{
public Amphibian(Tank tank)
: base(tank)
...{
}
public override void Shot()
...{
//do some Externsion
base.Shot();
}
public override void Run()
...{
//do some extension
base.Run();
}
}
Amphibian類繼承了Decorator類,在Amphibian類中的Shot和Run方法中,我們首先根據我們的功能,做一些shot和Run之前做一些相應的擴展,然後調用基類的Shot和Run方法,這樣做我們即做了相應的擴展,又調用基本的Shot和Run方法。
我們還可以根據其他功能擴展做擴展的類,並繼承Decorator類
如何來使用相關的功能呢?
static void Main(string[] args)
...{
Tank tank = new TigerTank();
Amphibian am = new Amphibian(tank);
am.Shot();
}
在上面的代碼中使用使用Amphibian來裝飾tank,那麼調用am.shot方法時,即擴展了兩棲功能又使用了tank的原本的shot功能。如果現在還有一個紅外功能要擴展,只需要實例化一個紅外類,使用兩棲類來作為參數,然後使用紅外類的shot方法,那麼這個shot即包含兩棲功能又包含紅外功能,並且還使用原來的shot功能。這裡就是組合多種功能,也就是說使用多種功能來裝飾tank。
Decorator模式的幾個要點
在.Net中Decorator模式的典型使用就是一系列的Stream類,作為實體類的有FileStream、NetworkStream、MemoryStream等,作為擴展功能接口的有BufferedStream、CryptoStream等。但是在Stream的擴展功能接口中是沒有類似Decorator基類的,他是在每個功能類中自己保存一個Stream實體對象,在方法中直接調用Stream實體對象的方法。