1. 程式人生 > >Decorator裝飾者模式(結構型模式)

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 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() { } } /// <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);//緩衝、機密的流