装饰者模式与继承的爱恨情仇

今天看了Head First 设计模式,然后看来看去,一直有一个疑问在我脑子里面久久的环绕。
就是装饰者模式能做到的,继承不是都可以完成吗?我们为什么要去使用装饰者模式。

首先看书上对装饰者模式的解释是这样的:

动态的将责任附加到对象上,想要扩展功能,装饰者提供有别于继承的另一种选择。

装饰者模式有以下几个特点:
1.扩展行为而无需修改现有的代码
2.装饰者类能反映出被装饰组件的类型
3.装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,从而达到特定的行为。

我感觉最主要的是书上这一部分的话:

利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。相反,如果能用组合的做法扩展对象的行为,就可以在运行时动态的进行扩展。

我在网上找了许多相关的博客,发现有一篇帮我理解清楚了。

大致意思如下:

现在有以下需求:

要实现自己的一套IO系统,实现文件的读取。没必要百分百的跟jdk的功能一模一样。

首先,有 MyReader 类//专门用于读取数据的类。
为什么会有这个类呢?
因为数据类型有很多,比如文本MyTextReader,媒体MyMediaReader,等等。就会向上提取公共方法类即MyReader,可以有如下继承关系:

MyReader//专门用于读取数据的类。
	|--MyTextReader
	|--MyMediaReader
	。。。。。。//扩展的继续继承添加

但是又考虑到效率问题,要使用缓冲来提高效率。对读方法复写,那么又有如下继承关系。

MyReader//专门用于读取数据的类。
	|--MyTextReader
		|--MyBufferTextReader
	|--MyMediaReader
		|--MyBufferMediaReader

上述的体系是可以的,OK的。但是,这个MyReader以后可能还会因为新的媒体文件而必须新增加,那么就又有如下的继承关系图。

MyReader//专门用于读取数据的类。
	|--MyTextReader
		|--MyBufferTextReader
	|--MyMediaReader
		|--MyBufferMediaReader
	|--MyDataReader
		|--MyBufferDataReader
	。。。。。。//扩展的继续继承添加

问题也随之而来,每次多一个子类,又得增加一个缓冲的实现子类。虽然这个体系是可以使用的。但是,扩展性不好,体系非常臃肿复杂。
那么就要对现有的体系进行优化。
所有的缓冲子类使用的技术都是相同的,没必要都给定义一个子类。现在可以直接单独定义一个缓冲类,如下:

/**
 * 专门为别人提高效率而存在的,所以,此类在初始化的时候,把被提高效率的对象传进来。
 */
class MyBufferReader {
    
    
    MyBufferReader(MyTextReader text) {
    
    
    }
 
    MyBufferReader(MyMediaReader media) {
    
    
    }
}

解释:
以前是有一个数据类型的类,就弄一个这个类的子类,弄个缓冲区来提高效率,来一个弄一个;
现在是定义一个缓冲区,大家公用一个缓冲区,需要提高谁的效率,把谁传进来。这样公共方法就可以得到复用。如上代码所示。
但是,上面的类的实现,扩展性很差。只要添加了新数据类型,那么就得新添构造函数。还得继续优化。
所以,又有如下的优化。
找到所有构造函数的参数的共同类型。通过多态的形式。可以提高扩展性。

/**
 * 通过多态的形式。可以提高扩展性。
 * 现在传入的参数就可以是MyTextReader、MyMediaReader、...
 * 后期再出现新的子类,也可以接受
 */
class MyBufferReader extends MyReader {
    
    
    private MyReader r;
 
    MyBufferReader(MyReader r) {
    
    
    }
}

为什么要继承 MyReader ?

新定义的类的作用,就是带着缓冲技术的reader,里面定义的也是读取行为,只是功能更强的读取行为。对读行为进行了增强。那么也是可以向上提升,也有读的动作。那么也是读的一份子,实现读的接口继承MyReader也是OK的。

最后,上述的继承体系,就变成了如下体系,新体系的名字就叫作装饰者模式:

MyReader//专门用于读取数据的类。
	|--MyTextReader
	|--MyMediaReader
	|--MyDataReader
	|--MyBufferReader

装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。(继承体系的时候,缓冲类,必须继承相应的类。现在就不需要啦,没有直接关系了。)

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。(可以理解为实现共同的接口,或继承一样父类)

结构上来说,由原来的继承结构,变成了现在的组合结构(我里面有你的结构)。具体怎么理解。再说吧。

装饰设计模式灵活在哪呢?
继承不要写的过多,不要以继承为主。产生子类过多,体系会显得非常臃肿。假设现在有个对象,是一年前写的,现在你新来到公司,觉得他这个类的功能不够用了。我自己写个类,把他的对象传进来,然后就可以进行加强了,当我写的功能有问题的时候,要是发现我的有问题的时候,就可以把我的注释掉,然后继续使用原来的。

总的来说,装饰者模式就减少子类的定义,讲想要的功能能够灵活的使用。

猜你喜欢

转载自blog.csdn.net/qq_43458555/article/details/108169274