设计模式-装饰模式
我们要明白使用装饰模式目的是什么,只有搞清目的我们才有思路进行下一步设计,那么既然叫做装饰,他的核心功能肯定是装饰这个类的。
我个人观点 装饰模式主要作用:
1:动态的为一个类添加功能(如果一个类不能被继承),并且可以随时撤销
2:改变类中某些对象并不影响其他的类
3:通过不同的具体装饰类来进行排列组合,创造更多的行为
我现在用笔的作用先简单的一步步说明装饰模式:
第一步: 定义一个接口
1 public interface IPen 2 { 3 string Write(); 4 }
第二步:基础类来实现这个接口(这个代表我们核心功能适用于普遍用户)
1 public class DefalutPen:IPen 2 { 3 public string Write() 4 { 5 return "我的核心功能就是写字"; 6 } 7 }
第三步:现在这个功能已经适用于大多用户,那么现在突然有个用户需要验证之后我们才可以写字,此时我们只能进行扩展
1 public class ValidationPen : IPen 2 { 3 private IPen pen; 4 private string validInfo; 5 public ValidationPen(IPen pen, string ValidInfo) 6 { 7 this.pen = pen; 8 this.validInfo = ValidInfo; 9 } 10 11 public string Write() 12 { 13 string s = ValidPen() + " "; 14 s += this.pen.Write(); 15 s = "验证类:" + s; 16 return s; 17 } 18 19 private string ValidPen() 20 { 21 if (this.validInfo == "admin") 22 { 23 return "验证成功"; 24 } 25 else 26 { 27 return "验证失败"; 28 } 29 } 30 31 }
第四步:但是现在又有一部分人不需要验证,但是需要加密,然后才可以使用笔的功能,那么此时我们在次进行扩展
1 public class EncryptionPen:IPen 2 { 3 private IPen pen; 4 private string mode; 5 6 public EncryptionPen(IPen pen, string Mode) 7 { 8 this.pen = pen; 9 this.mode = Mode; 10 } 11 12 public string Write() 13 { 14 string s = Encryption() + " "; 15 s += pen.Write(); 16 s = "加密类:" + s; 17 return s; 18 } 19 20 private string Encryption() 21 { 22 string result = string.Empty; 23 if (mode=="jm") 24 { 25 result = "正在加密"; 26 } 27 else 28 { 29 result = "正在解密"; 30 } 31 return result; 32 } 33 }
第五步:现在我们基本通过上述明白装饰模式的一个用法 现在看一下效果
第六步:回顾装饰模式作用:第一个:动态的为类添加额外的功能 比喻加密类,验证类,同时便于我们撤销。第二个:改变其中一个类并不影响其他类的功能,耦合性很低这是一个重大的优点。
第三个:根据不同的组合来实现更多的功能 我们可以把验证类和加密类当做参数传递 那么就会这个类的功能就有验证和加密了。有了很多的扩展性。
第七步:我们来看一个微软使用的装饰模式(Stream,DeflateStream(提供 Deflate算法压缩和解压缩的流方法)以及BufferedStream(缓冲流)) 我有选择的粘贴 大家如果有兴趣可以用反编译软件可以去看
1 public abstract class Stream : IDisposable { 2 public static readonly Stream Null = new NullStream(); 3 private const int _DefaultCopyBufferSize = 81920; 4 [NonSerialized] 5 private ReadWriteTask _activeReadWriteTask; 6 [NonSerialized] 7 private SemaphoreSlim _asyncActiveSemaphore; 8 9 internal SemaphoreSlim EnsureAsyncActiveSemaphoreInitialized() 10 { 11 12 return LazyInitializer.EnsureInitialized(ref _asyncActiveSemaphore, () => new SemaphoreSlim(1, 1)); 13 } 14 15 public abstract bool CanRead { 16 [Pure] 17 get; 18 } 19 20 public abstract bool CanSeek { 21 [Pure] 22 get; 23 } 24 public void CopyTo(Stream destination) 25 { 26 if (destination == null) 27 throw new ArgumentNullException("destination"); 28 if (!CanRead && !CanWrite) 29 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed")); 30 if (!destination.CanRead && !destination.CanWrite) 31 throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed")); 32 if (!CanRead) 33 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream")); 34 if (!destination.CanWrite) 35 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream")); 36 Contract.EndContractBlock(); 37 38 InternalCopyTo(destination, _DefaultCopyBufferSize); 39 } 40 }
从上述代码可以看出Stream是一个抽象类(相当于我们的接口)但是Stream里面有抽象方法和虚方法两个模式(我们可以根据实际情况选择)再来看看一部DeflateStream类
1 public class DeflateStream : Stream { 2 3 internal const int DefaultBufferSize = 8192; 4 private Stream _stream; 5 private CompressionMode _mode; 6 private bool _leaveOpen; 7 private Inflater inflater; 8 private IDeflater deflater; 9 private byte[] buffer; 10 11 private int asyncOperations; 12 private readonly AsyncCallback m_CallBack; 13 private readonly AsyncWriteDelegate m_AsyncWriterDelegate; 14 15 private IFileFormatWriter formatWriter; 16 private bool wroteHeader; 17 private bool wroteBytes; 18 19 private enum WorkerType : byte { Managed, ZLib, Unknown }; 20 private static volatile WorkerType deflaterType = WorkerType.Unknown; 21 22 23 public DeflateStream(Stream stream, CompressionMode mode) 24 : this(stream, mode, false) { 25 } 26 27 public DeflateStream(Stream stream, CompressionMode mode, bool leaveOpen) { 28 29 if(stream == null ) 30 throw new ArgumentNullException("stream"); 31 32 if (CompressionMode.Compress != mode && CompressionMode.Decompress != mode) 33 throw new ArgumentException(SR.GetString(SR.ArgumentOutOfRange_Enum), "mode"); 34 35 _stream = stream; 36 _mode = mode; 37 _leaveOpen = leaveOpen; 38 39 switch (_mode) { 40 41 case CompressionMode.Decompress: 42 43 if (!_stream.CanRead) { 44 throw new ArgumentException(SR.GetString(SR.NotReadableStream), "stream"); 45 } 46 47 inflater = new Inflater(); 48 49 m_CallBack = new AsyncCallback(ReadCallback); 50 break; 51 52 case CompressionMode.Compress: 53 54 if (!_stream.CanWrite) { 55 throw new ArgumentException(SR.GetString(SR.NotWriteableStream), "stream"); 56 } 57 58 deflater = CreateDeflater(null); 59 60 m_AsyncWriterDelegate = new AsyncWriteDelegate(this.InternalWrite); 61 m_CallBack = new AsyncCallback(WriteCallback); 62 63 break; 64 65 } // switch (_mode) 66 67 buffer = new byte[DefaultBufferSize]; 68 } 69 70 public DeflateStream(Stream stream, CompressionLevel compressionLevel) 71 72 : this(stream, compressionLevel, false) { 73 } 74 75 // Implies mode = Compress 76 public DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen) { 77 78 if (stream == null) 79 throw new ArgumentNullException("stream"); 80 81 if (!stream.CanWrite) 82 throw new ArgumentException(SR.GetString(SR.NotWriteableStream), "stream"); 83 84 // Checking of compressionLevel is passed down to the IDeflater implementation as it 85 // is a pugable component that completely encapsulates the meaning of compressionLevel. 86 87 Contract.EndContractBlock(); 88 89 _stream = stream; 90 _mode = CompressionMode.Compress; 91 _leaveOpen = leaveOpen; 92 93 deflater = CreateDeflater(compressionLevel); 94 95 m_AsyncWriterDelegate = new AsyncWriteDelegate(this.InternalWrite); 96 m_CallBack = new AsyncCallback(WriteCallback); 97 98 buffer = new byte[DefaultBufferSize]; 99 } 100 public override bool CanWrite { 101 get { 102 if( _stream == null) { 103 return false; 104 } 105 106 return (_mode == CompressionMode.Compress && _stream.CanWrite); 107 } 108 } 109 }
这个类就是对流的扩展我们可以看出里面有大量的扩展主要是进行压缩和解压缩的方法
1 public sealed class BufferedStream : Stream { 2 private const Int32 _DefaultBufferSize = 4096; 3 private Stream _stream; 4 private Byte[] _buffer; 5 private readonly Int32 _bufferSize; 6 private Int32 _readPos; 7 private Int32 _readLen; 8 private Int32 _writePos; 9 private BeginEndAwaitableAdapter _beginEndAwaitable; 10 private Task<Int32> _lastSyncCompletedReadTask; 11 private BufferedStream() { } 12 public BufferedStream(Stream stream) 13 14 : this(stream, _DefaultBufferSize) { 15 } 16 17 18 public BufferedStream(Stream stream, Int32 bufferSize) { 19 20 if (stream == null) 21 throw new ArgumentNullException("stream"); 22 23 if (bufferSize <= 0) 24 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", "bufferSize")); 25 26 Contract.EndContractBlock(); 27 28 BCLDebug.Perf(!(stream is FileStream), "FileStream is buffered - don't wrap it in a BufferedStream"); 29 BCLDebug.Perf(!(stream is MemoryStream), "MemoryStream shouldn't be wrapped in a BufferedStream!"); 30 BCLDebug.Perf(!(stream is BufferedStream), "BufferedStream shouldn't be wrapped in another BufferedStream!"); 31 _stream = stream; 32 _bufferSize = bufferSize; 33 if (!_stream.CanRead && !_stream.CanWrite) 34 __Error.StreamIsClosed(); 35 } 36 public override bool CanWrite { 37 [Pure] 38 get { return _stream != null && _stream.CanWrite; } 39 } 40 }
这个类主要针对缓冲流的。我们可以看出DeflateStream和BufferedStream没什么联系其中一个改变不会导致另一个的变化 而且很容易扩展。这三个类并不很全自己可以去研究
第八步:总结:
1:一个类不能被继承,但是我们还需要动态的添加额外的功能那么装饰模式就是我们的首选。
2:既然不用继承就可以额外添加功能,为嘛我们还是继承公共接口。 我个人觉得这样做的好处1是统一一种规范,2是如果我们装饰被装饰的类那么我们就可以把被装饰的类当参数传递,我们就统一了接口。
3:继承也能完成这些功能为啥要用装饰模式。 主要从耦合度考虑我们使用继承 那么会在底层进行大量的逻辑判断 这样一来代码就极难维护,用装饰模式就避免了这些问题,因为他们都具有自己不同的装饰功能,
还有就是如果使用继承,如果基类修改了那么所有的代码都有影响(也许其他类根本不需要,使用装饰模式完全可以不理会新添加的功能。只需要改动需要的装饰类)