什么是装饰器模式

什么是装饰器模式

装饰器模式的说明:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。原文是:

Attach additional responsibilities to an object dynamically keeping
the same interface.Decorators provide a flexible alternative to
subclassing for extending functionality.

从这段话可以看到装饰器的特点:动态地为对象增加新的功能,即扩展原有对象的功能。
装饰器的优点,至少是相比于继承来说,“更有弹性”,怎么理解呢,装饰器模式相比生成子类更为灵活,简单理解就是继承扩展是静态的,是在编译期间确定的,而装饰器是“动态”的,是在运行时灵活装配出来的。

就以美国电影《钢铁侠》为例,如果托尼·史塔克在没有造出战衣之前,在考虑想拥有一个能够喷火的功能,那么如果采用继承的方式,则需要他生下一个儿子,该儿子通过基因变异出现了喷火功能,如果他又想拥有飞行功能怎么办呢,原来那个儿子的基因已经确定了,不可能拥有飞行的功能了,怎么办呢,只能再生一个儿子,拥有飞行的基因才行,想想都不灵活。这时候装饰模式来解决这个问题,托尼·史塔克不需要“生儿子”动作那么大,只需要打造一个支持扩展功能的钢铁战衣(装饰器)就可以,他自身相当于是具体的待装饰的组件,当需要喷火时,只需要在战衣上增加喷火器就可以,如果又想飞行,只需要在战衣上增加飞行装置就行,当然,如果不想喷火了,拆掉就行了,灵活。
如下图所示为继承图:
什么是装饰器模式_第1张图片
如下图所示为装饰器模式图:
什么是装饰器模式_第2张图片

按照UML图来展示如下图所示:

什么是装饰器模式_第3张图片

代码如下所示:
1.人形接口定义了所有实现人形接口的子类均需要实现action方法。

package com.sxfang.abc;

public interface IPeopleLikeInterface {
    public void action();
}

2.TonyStark子类实现了IPeopleLikeInterface接口,定义了TonyStark所具有的行为能力:

package com.sxfang.abc;

public class TonyStark implements IPeopleLikeInterface {
    @Override
    public void action() {
        System.out.println("I am TonyStark");
    }
}

3.BattleSuit抽象类,同样实现了IPeopleLikeInterface 接口,与TonyStark不同的是,BattleSuit抽象类还包括一个IPeopleLikeInterface 接口的引用,该引用就是需要被装饰(或被增强)的对象的引用。

package com.sxfang.abc;

public    class BattleSuit implements  IPeopleLikeInterface {
    private IPeopleLikeInterface people;
    @Override
    public void action() {
    }
    public IPeopleLikeInterface getPeople() {
        return people;
    }
    public void setPeople(IPeopleLikeInterface people) {
        this.people = people;
    }
}

4.相应的FireSuit子类,继承自BattleSuit抽象类,实现了自己的action方法,该行为就是增强或装饰的功能。

package com.sxfang.abc;

public class FireSuit extends  BattleSuit {

    @Override
    public void action()
    {
        super.getPeople().action();
        System.out.println("Fire on !!");
    }
}

5.与FireSuit子类类似的是可飞行功能增强,如下图所示:

package com.sxfang.abc;

public class FlySuit extends  BattleSuit {

    @Override
    public void action()
    {
        super.getPeople().action();
        System.out.println("Fly !!");
    }
}

至此对象间的结构构建完成,那么在实际使用过程中是如何动态增强TonyStark的功能的呢,如下图所示:

public class DecorateRun {
    public void main(String[] args)
    {
        /**
         * 初始化一个TonyStark对象
         */
        IPeopleLikeInterface tonyStark = new TonyStark();
        /**
         * 当需要使TonyStark具有喷火功能时
         */
        BattleSuit battleSuit = new FireSuit();
        battleSuit.setPeople(tonyStark);
        /**
         * 使用喷火功能
         */
        battleSuit.action();

        /**
         * 当需要使TonyStark具有飞行功能时
         */
        BattleSuit battleSuit2 = new FlySuit();
        battleSuit.setPeople(tonyStark);
        /**
         * 使用飞行功能
         */
        battleSuit2.action();
    }
}

好了,接下来我们看一下成熟的java 源码中流机制是如何使用装饰模式的。

java语言采用流的机制来实现输入/输出。所谓流,就是数据的有序流动,流可以是从某个源出来,到某个目的地去。根据流的方向可以将流分成输出流和输入流。程序通过输入流读取数据,通过输出流写出数据。
例如:一个java程序可以使用FileInputStream类从一个磁盘文件读取数据,如下图:
什么是装饰器模式_第4张图片

查看源码可知FileInputStream从磁盘文件中直接读取文件,如下图所示为最基础用法:

FileInputStream inputStream = new FileInputStream("d://1.txt");
int len;
/**
 * 一次读取字节,每读取一个字节都要实现一次与硬盘的交互操作
 *
 */
while ((len = inputStream.read()) != -1) {

}

当然也可以每次读取更多字节,以减少与硬盘的IO交互次数,如下图所示:

FileInputStream inputStream = new FileInputStream("d://1.txt");
int len;
byte[] bs = new byte[1024];
//这里添加了一个缓存数组,每次从硬盘读取1024个字节,也就是说,每读取1024个字节才与硬盘实现一次交互
while ((len = inputStream.read(bs)) != -1) {
    
}

实际上java提供了一种更为快速的实现方案,即引入BufferInputStream,如下图所示:

FileInputStream inputStream = new FileInputStream("d://1.txt");
BufferedInputStream bis = new BufferedInputStream(inputStream); //默认有8M的缓存    
int len;
byte[] bs = new byte[1024];
/**
 *  先从硬盘读出8M到缓存中。然后read,这里的read并不是从硬盘中读取,而是从那8M缓存(内存)中读取,
 *  自然要比从硬盘中快得多。8M缓存用完后又会从硬盘补充(也就是说,一次从硬盘获取8M字节的数据)。
 *  8M与硬盘交互一次
 */      
while ((len = bis.read(bs)) != -1) {
 
}

很显然,BufferInputStream就是一个装饰器,FileInputStream一是原始对象,类似于TonyStark的角色,通过BufferInputStream来对FileInputStream的功能进行增强。

如下图所示为IO 输入流的类层级关系:
什么是装饰器模式_第5张图片

其中
ByteArrayInputStream、
FileInputStream、
ObjectInputStream、
PipedInputStream、
SequenceInputStream、
StringBufferInputStream均是原始对象。
FilterInputStream就是抽象的装饰类,
BufferedInputStream、DataInputStream、LineNumberInputStream、PushbackInputStream 均是实现FilterInputStream的具体装饰类,用于装饰或增强原始对象的功能。

你可能感兴趣的:(设计模式,设计模式)