Decorator裝飾者模式(結構型模式)
1、需求
假設讓我們去設計FCL中的Stream類,該類具有流類的基本功能,除了有各種不同型別的流外(如記憶體流、檔案流、網路流等等),但是在不同的業務場景下,如處理銀行業務,需要給相關的記憶體流進行加密操作,給相關的銀行視訊業務,進行視訊流加密操作.
2、通常性的做法
/// <summary> /// 流抽象 /// </summary> public abstract class Stream { /// <summary> ///讀取流的方法 /// </summary> public abstract void Read(); /// <summary> /// 流的長度 /// </summary> public abstract long Length { get; } } /// <summary> /// 記憶體流 /// </summary> public classMemoryStream : Stream { public override long Length => 1000000000000000; public override void Read() { } /// <summary> /// 定義自己的實現 /// </summary> public virtual void Write() { } } /// <summary> /// 檔案流/// </summary> public class FileStream : Stream { public override long Length => 1000000000000000; public override void Read() { } /// <summary> /// 定義自己的實現 /// </summary> public virtual void Write() { } } /// <summary> /// 加密約束介面 /// </summary> public interface ICryto { /// <summary> /// 機密方法 /// </summary> void Cryto(); } /// <summary> /// 緩衝約束介面 /// </summary> public interface IBuffered { /// <summary> /// 緩衝方法 /// </summary> void Buffered(); } /// <summary> /// 加密記憶體流 /// </summary> public class CryptoMemoryStream : MemoryStream, ICryto { public override long Length => 1000000000000000; public void Cryto() { } public override void Read() { } } /// <summary> /// 加密緩衝記憶體流 /// </summary> public class CryptBufferedMemoryStream : MemoryStream, ICryto, IBuffered { public override long Length => 100000000000; public void Buffered() { } public void Cryto() { } public override void Read() { } } /// <summary> /// 加密檔案流 /// </summary> public class CryptoFileStream : FileStream, ICryto { public override long Length => 1000000000000000; public void Cryto() { } public override void Read() { } } /// <summary> /// 加密緩衝檔案流 /// </summary> public class CryptBufferedFileStream : FileStream, ICryto, IBuffered { public override long Length => 100000000000; public void Buffered() { } public void Cryto() { } public override void Read() { } }
ok,上面的設計符合我們的需求,但是如果這個時候多了一個網路流NetStream,而且這個類也需要加密和加密緩衝的功能,這個時候,就需要在寫3個子類,如何流的擴充套件功能增多,有需要額外編寫更多的子類來滿足需求,這樣下去,子類會以指數級增長,所以,顯然這種設計是不可取的.
3、問題
由於上面的設計過多的使用了繼承來擴充套件物件的功能,由於繼承本身的缺陷,使得這種擴充套件方式缺乏靈活性,並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹(多繼承,至繼承一個類,但是實現了多個介面).
那麼如何使"物件功能的擴充套件"能夠根據需要動態的實現,同時避免功能擴充套件的同時,子類的膨脹?
4、Decorator裝飾者模式
/// <summary> /// 流抽象 /// </summary> public abstract class Stream { /// <summary> /// 讀取流的方法 /// </summary> public abstract void Read(); /// <summary> /// 流的長度 /// </summary> public abstract long Length { get; } } /// <summary> /// 記憶體流 /// </summary> public class MemoryStream : Stream { public override long Length => 1000000000000000; public override void Read() { } /// <summary> /// 定義自己的實現 /// </summary> public virtual void Write() { } } /// <summary> /// 檔案流 /// </summary> public class FileStream : Stream { public override long Length => 1000000000000000; public override void Read() { } /// <summary> /// 定義自己的實現 /// </summary> public virtual void Write() { } } public abstract class StreamDecorator : Stream//介面繼承 { private Stream _stream; public StreamDecorator(Stream stream) { _stream = stream; } public override long Length => 1000000000000; public override void Read() { } } /// <summary> /// 加密功能裝飾器 /// </summary> public class CrytoDecorator : StreamDecorator { public CrytoDecorator(Stream stream) : base(stream) { } public override long Length => base.Length; public override void Read() { //這裡做加密功能的擴充套件或者不做,直接呼叫父類的Read操作 base.Read(); } } /// <summary> /// 緩衝功能裝飾器 /// </summary> public class CrytoBufferedDecorator : StreamDecorator { public CrytoBufferedDecorator(Stream stream) : base(stream) { } public override long Length => base.Length; public override void Read() { //這裡做緩衝功能的擴充套件或者不做,直接呼叫父類的Read操作 base.Read(); } }
客戶端呼叫程式碼如下:
public class ThirdSystem { public void Run() { var fs = new FileStream(); var crytoStream = new CrytoDecorator(fs);//加密檔案流 var crytoBufferedDecorator = new CrytoBufferedDecorator(crytoStream);//加密緩衝檔案流 var ms = new MemoryStream(); var crytoMsStream = new CrytoDecorator(ms);//加密記憶體流 var MsCrytoBufferedDecorator = new CrytoBufferedDecorator(crytoMsStream);//加密緩衝記憶體流 } }
5、裝飾者模式的作用
(1)、主要解決主體類在多個方向上的擴充套件問題,並非解決多繼承產生的"子類氾濫"的問題.
(2)、通過採用組合而非繼承的方式,實現了在執行時動態的擴充套件物件功能的能力,可以更具需要擴充套件多個功能,避免了使用繼承帶來的"靈活性差"和"子類氾濫"的問題.
(3)、Stream類在Decorator模式中充當抽象介面的角色,不應該去實現具體的行為,Stream類無需知道Decorator類,Decorator類是從外部來擴充套件Stream類的功能.
(4)、Decorator類在程式碼表現上是is a Stream的繼承關係,即Decorator繼承了Stream類所具有的所有的介面,但是實現上有表現為Has a的關係,即裝飾著擁有一個Stream類,可以使用一個或者多個裝飾者來包裝Stream類,但最終還是隻有一個Stream類.
6、實際上微軟在設計流系統時,就是使用了這種方式,具體看如下程式碼:
MemoryStream ms = new MemoryStream(new byte[] {1,2,3,4 });//記憶體流 BufferedStream bf = new BufferedStream(ms);//緩衝的記憶體流 CryptoStream cs = new CryptoStream(bf, null,CryptoStreamMode.Read);//緩衝、機密的流