• 不一样的装饰器模式(设计模式二)


    前言

    我是一名博客园的博主,目前排名第199675位,虽然排名稍稍落后,但这并不影响我向大家学习然后自己吹水。

    在常见的设计模式中,每个项目或者说产品可以说装饰器几乎必用。

    装饰器是decorator,别名wrapper,也称为包装器,是创建型、结构型、行为型分类的结构型,具体有什么用呢?

    说有什么用,不如直接看下,通过使用装饰器有什么结构变化,是如何演化过来的。

    通过演化过程,自然知道为什么装饰器划分为结构型,装饰器解决了什么问题,作用是啥。

    上车出发

    在流中,有文件流、内存流以及网络流,他们的读取方式都不一样,但是它们都继承steam 这个类。
    像这样:

    public abstract class Stream
    {
    	protected abstract char Read(int number);
    
    	protected abstract void seek(int position = 0);
    
    	protected virtual void write(byte data) { }
    }
    

    假设让我们来分别设计文件流、内存流与网络流
    文件流:

    public class FileStream: Stream
    {
    
    	public virtual void write(byte data) {
    		// 写入文件
    	}
    
    	public override char Read(int number)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override void seek(int position = 0)
    	{
    		throw new NotImplementedException();
    	}
    }
    

    内存流:

    public class MemoryStream:Stream
    {
    
    	public virtual void write(byte data)
    	{
    		// 写内存流
    	}
    
    	public override char Read(int number)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override void seek(int position = 0)
    	{
    		throw new NotImplementedException();
    	}
    }
    

    网络流:

    public class NetworkStream: Stream
    {
    
    	public virtual void write(byte data)
    	{
    		// 写网络流
    	}
    
    	public override char Read(int number)
    	{
    		throw new NotImplementedException();
    	}
    
    	public override void seek(int position = 0)
    	{
    		throw new NotImplementedException();
    	}
    }
    

    我们在读取或者写入这些流的时候,可能会进行加密或者说缓存。
    第一版设计,假设我们以继承的方式实现。
    加密的文件流:

    public class CryptoBufferedFileStream: FileStream
    {
    	public override  char Read(int number)
    	{
    		base.Read(number);
    		//额外的加密操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的加密操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的加密操作...
    		base.write(data);
    	}
    }
    

    加密的内存流:

    public class CryptoMemoryStream : MemoryStream
    {
    	public override char Read(int number)
    	{
    		base.Read(number);
    		//额外的加密操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的加密操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的加密操作...
    		base.write(data);
    	}
    }
    

    加密的网络流

    public class CryptoNetworkStream : NetWorkStream
    {
    	public override char Read(int number)
    	{
    		base.Read(number);
    		//额外的加密操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的加密操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的加密操作...
    		base.write(data);
    	}
    }
    

    同样我们还要实现缓存流:

    //文件流缓冲
    public class BufferedFileStream: FileStream
    {
    	public override char Read(int number)
    	{
    		base.Read(number);
    		//额外的缓冲操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的缓冲操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的缓冲操作...
    		base.write(data);
    	}
    }
    //内存流缓冲
    public class BufferedMemoryStream : MemoryStream
    {
    	public override char Read(int number)
    	{
    		base.Read(number);
    		//额外的缓冲操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的缓冲操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的缓冲操作...
    		base.write(data);
    	}
    }
    //网络流缓冲
    public class BufferedNetworkStream : NetWorkStream
    {
    	public override char Read(int number)
    	{
    		base.Read(number);
    		//额外的缓冲操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的缓冲操作
    		base.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的缓冲操作...
    		base.write(data);
    	}
    }
    

    写完之后,会发现几个致命性问题:

    1.没有实现我们要加密也要缓存,如果要写下去那么还需要3个类,感觉没有尽头。

    假设steam 有n 个继承基础类,如FileSteam,需要实现m个扩展功能,比如说加密缓冲等。
    那么计算是n(C(m,1),c(m,2)....,C(m,m))
    计算C(m,1),c(m,2)....,C(m,m)=2^m-1
    答案是n(2^m-1)
    不好意思,我的数学不是很好,如果有错误,望请指正。
    2.无论加密还是缓存都是操作byte,而且操作都是read、write与seek,感觉有太多相似性代码了。

    根据这两点我们改一下。
    加密流:

    public class CryptoBufferedStream:Stream
    {
    	Stream stream;
    
    	public CryptoBufferedStream(Stream stream)
    	{
    		this.stream = stream;
    	}
    
    	public override char Read(int number)
    	{
    		stream.Read(number);
    		//额外的加密操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的加密操作
    		stream.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的加密操作...
    		stream.write(data);
    	}
    }
    

    缓冲流:

    public class BufferedMemoryStream : Stream
    {
    	Stream stream;
    
    	public BufferedMemoryStream(Stream stream) {
    		this.stream = stream;
    	}
    
    	public override char Read(int number)
    	{
    		stream.Read(number);
    		//额外的缓冲操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的缓冲操作
    		stream.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的缓冲操作...
    		stream.write(data);
    	}
    }
    

    这样似乎就看起来舒服很多了,这里可能会有一个疑惑性的问题,
    为什么还要继承Stream?
    装饰器也叫包装器,我们使用CryptoBufferedStream把文件流包装成了加密文件流,包装完也是必须是流啊,打包完别忘了打标签哦。

    调用示例:

    static void Main(string[] args)
    {
    	FileStream fileStream = new FileStream();
    	BufferedMemoryStream bufferedMemoryStream = new BufferedMemoryStream(fileStream);
    }
    

    这样就把fileStream给包装成了bufferedMemoryStream,后面我们使用的就是BufferedMemoryStream。
    这样我们没有失去fileSteam 是流的特性,而且还要另外一个好处,既然还是流,那么我们还可以继续包装啊。

    CryptoBufferedStream cryptoBufferedStream = new CryptoBufferedStream(bufferedMemoryStream);
    

    这时候我们即加密了,也缓冲了,实现了我们原来没有写的3个类。
    这里发现一个问题,那就是stream这个属性在CryptoBufferedStream 和 BufferedMemoryStream 都存在。
    其实提取和不提取都没有关系,但是对我们写代码的人来说,每次都要去写个可以提取出来的字段,是相当不舒服的,而且还不开心。
    终版:
    将stream属性提取出来,提取到DecoratorStream中。

    public abstract class DecoratorStream: Stream
    {
    	protected Stream stream;
    	public DecoratorStream(Stream stream){
    		this.stream = stream;
    	}
    }
    
    public class CryptoBufferedStream: DecoratorStream
    {
    
    	public CryptoBufferedStream(Stream stream):base(stream)
    	{
    	}
    
    	public override char Read(int number)
    	{
    		stream.Read(number);
    		//额外的加密操作
    		throw new NotImplementedException();
    	}
    	public override  void seek(int position)
    	{
    		//额外的加密操作
    		stream.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的加密操作...
    		stream.write(data);
    	}
    }
    
    public class BufferedMemoryStream : DecoratorStream
    {
    
    	public BufferedMemoryStream(Stream stream):base(stream) {
    
    	}
    
    	public override char Read(int number)
    	{
    		stream.Read(number);
    		//额外的缓冲操作
    		throw new NotImplementedException();
    	}
    	public override void seek(int position)
    	{
    		//额外的缓冲操作
    		stream.seek(position);
    	}
    	public override void write(byte data)
    	{
    		//额外的缓冲操作...
    		stream.write(data);
    	}
    }
    

    看起来就舒服很多了。
    为什么看起来会舒服,一张图解释出来。

    假设steam 有n 个继承基础类,如FileSteam,需要实现m个扩展功能,比如说加密缓冲等。按照装饰器的计算方式是:m+1
    n(2^m-1)与m+1 两者的方式对比显而易见啊,现在可以证明为什么装饰器是一个结构模式了吧。
    至于作用,可以看出,可以解决继承导致的子类膨胀问题。

    uml图

    后面补上,画图累啊。

    总结

    因为n(2m-1)在n>1或者m大于1的情况下,n(2m-1)=>m+1,且仅当m=2时候两者相等,所以作用为:可以解决继承导致的子类膨胀问题,膨胀问题属于结构问题,解决的是结构问题,所以归属为结构型。

  • 相关阅读:
    L161
    L160
    L159
    PyQt编程实战:画出QScrollArea的scrollAreaWidgetContents内容部署层的范围矩形
    PyQt(Python+Qt)学习随笔:QScrollArea滚动区域layout布局的作用及设置方法
    PyQt(Python+Qt)学习随笔:QScrollArea滚动区域的scrollAreaWidgetContents、widget及setWidget等相关概念解释
    PyQt(Python+Qt)学习随笔:怎么在QScrollArea滚动区域中展示子部件的超长内容?
    PyQt(Python+Qt)学习随笔:QScrollArea的widgetResizable属性
    PyQt(Python+Qt)学习随笔:QScrollArea的alignment属性不起作用的原因
    第二十四章、containers容器类部件QScrollArea滚动区域详解
  • 原文地址:https://www.cnblogs.com/aoximin/p/12078650.html
Copyright © 2020-2023  润新知