详细讲解装饰者模式和继承之间的关系和优劣(讲道理这事儿,不怕过 就怕不够)

在上一个装饰者模式简单实例中,可能有的朋友会觉得:使用继承不也是可以实现的嘛,干嘛这么麻烦。具体如下:
新建一个类,继承PersonBefore,然后,在子类里面调用super.eat(),也是可以直接调用父类的方法,也没有修改父类内容实现代码。

那么,这就来说说继承和装饰者模式之间的区别,为什么选择装饰者模式,而不使用继承来实现这个需求。也就是为什么装饰者设计模式会比继承好。

先来说说继承和装饰设计模式谁先出来,那必须是先是继承先出来的。

现在有以下需求:
要实现自己的一套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


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

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

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

装饰设计模式灵活在哪呢?
继承不要写的过多,不要以继承为主。产生子类过多,体系会显得非常臃肿。假设现在有个对象,是一年前写的,现在你新来到公司,觉得他这个类的功能不够用了。我自己写个类,把他的对象传进来,然后就可以进行加强了,当我写的功能有问题的时候,要是发现我的有问题的时候,就可以把我的注释掉,然后继续使用原来的。就如同上一次的例子里面,扩展的PersonNow的方法发生了问题,那么可以注释掉不用,继续使用PersonBefore.eat();模块功能还在。项目继续可以使用。

下面把上次的例子,做个简单的升级。
有以下四个类:
Person  共同接口,实现多态用。
PersonBefore  被装饰对象类
PersonNow 装饰类
PersonDemo   装饰模式测试类

package com.lxk.designPattern.decorator;

/**
 * 公共接口(为实现多态)
 * 

* Created by lxk on 2016/11/24 */ public interface Person { void eat(); }


package com.lxk.designPattern.decorator;

/**
 * 被装饰的类
 * (被增强对象)
 * 

* Created by lxk on 2016/11/24 */ public class PersonBefore implements Person { @Override public void eat() { System.out.println("吃饭"); } }


package com.lxk.designPattern.decorator;

/**
 * 装饰类
 * (要去增强目标对象的类)
 * 装饰类通常会通过构造方法接收被装饰的对象。
 * 并基于被装饰的对象的功能,提供更强的功能。
 * 

* Created by lxk on 2016/11/24 */ public class PersonNow implements Person { private PersonBefore p; PersonNow(PersonBefore p) { this.p = p; } @Override public void eat() { //简单扩展 System.out.println("开胃酒"); p.eat(); System.out.println("甜点"); System.out.println("来一根"); } }


package com.lxk.designPattern.decorator;

/**
 * 装饰设计模式:
 * 当想要对已有的对象进行功能增强时,
 * 可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
 * 那么自定义的该类称为装饰类。
 * 

* 装饰类通常会通过构造方法接收被装饰的对象。 * 并基于被装饰的对象的功能,提供更强的功能。 */ class PersonDemo { public static void main(String[] args) { PersonBefore p = new PersonBefore(); //p.eat();//这个是需求1.0 PersonNow sp = new PersonNow(p); sp.eat(); } }
















你可能感兴趣的:(design,pattern)