设计模式之 装饰器模式

装饰器模式(Decorator Pattern),在不改变原有的一个类的情况下,动态的扩展一个对象的功能。

装饰器通过持有一个对象,将其包裹起来,并增强这个对象的功能。

看上面的说明并不好理解,我们举个例子。

比如平时我们吃的馒头,有各式各样的,比如普通的白馒头,还有加了糖的甜馒头,还有捏了各种造型的馒头,比如外形是小兔子馒头,小燕子馒头等。

按照常规的实现,我们可能是先写一个馒头制作步骤的接口,各种不同馒头类去实现接口中的馒头制作方法,new哪种馒头,就制作哪种馒头。

用装饰器模式后,我们做甜味馒头,其步骤和方法也是基于普通馒头的做法,稍加更改,就能完成甜味馒头的制作。所以我们制作甜味馒头,就让甜味馒头的制作类中持有一个普通馒头的对象,在需要更改步骤的方法中做修改,这样就相当于增强了原来普通馒头中某些步骤额功能,从而制作出新品种的馒头。

来看代码演示。

1、定义馒头制作接口

package com.itzhimei.study.design.decorator;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public interface ISteamedBun {

    /**
     * 准备材料
     */
    void prepare();

    /**
     * 和面
     */
    void kneadDough();

    /**
     * 蒸馒头
     */
    void steamed();

    /**
     * 做馒头
     */
    void make();
}

2、制作普通馒头

package com.itzhimei.study.design.decorator;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class NormalSteamedBun implements ISteamedBun {

    @Override
    public void prepare() {
        System.out.println("准备食材:发面...");
    }

    @Override
    public void kneadDough() {
        System.out.println("和面...");
    }

    @Override
    public void steamed() {
        System.out.println("蒸馒头,出锅...");
    }

    @Override
    public void make() {
        this.prepare();
        this.kneadDough();
        this.steamed();
    }
}

2、制作甜味馒头,我们在和面的时候,加了糖,最后的馒头就是甜味的了

package com.itzhimei.study.design.decorator;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class SweetSteamedBun extends NormalSteamedBun {

    NormalSteamedBun steamedBun;

    public SweetSteamedBun(NormalSteamedBun steamedBun) {
        this.steamedBun = steamedBun;
    }

    /**
     * 加糖的步骤当然也可以放到发酵的阶段,个人觉得放在和面接阶段
     */
    @Override
    public void kneadDough() {
        System.out.println("面粉中加糖,调甜味...");
        steamedBun.kneadDough();
    }
}

3、制作小鸟造型馒头,这里也是一样,和完面,把馒头做出小鸟造型

package com.itzhimei.study.design.decorator;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class BirdSteamedBun extends NormalSteamedBun {

    NormalSteamedBun steamedBun;

    public BirdSteamedBun(NormalSteamedBun steamedBun) {
        this.steamedBun = steamedBun;
    }

    @Override
    public void kneadDough() {
        steamedBun.kneadDough();
        System.out.println("和面后,开始给馒头做成小鸟造型...");
    }
}

4、测试(仔细观察测试代码中制作小鸟造型甜味馒头的代码

package com.itzhimei.study.design.decorator;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        NormalSteamedBun normalSteamedBun = new NormalSteamedBun();

        System.out.println("------【甜味】馒头制作开始------");
        SweetSteamedBun sweetSteamedBun = new SweetSteamedBun(normalSteamedBun);
        sweetSteamedBun.make();
        System.out.println("------【甜味】馒头制作结束------");

        System.out.println("------小鸟造型【普通】馒头制作开始------");
        BirdSteamedBun normalBirdSteamedBun = new BirdSteamedBun(normalSteamedBun);
        normalBirdSteamedBun.make();
        System.out.println("------小鸟造型【普通】馒头制作结束------");

        System.out.println("------小鸟造型【甜味】馒头制作开始------");
        BirdSteamedBun birdSweetSteamedBun = new BirdSteamedBun(sweetSteamedBun);
        birdSweetSteamedBun.make();
        System.out.println("------小鸟造型【甜味】馒头制作结束------");
    }
}

输出:

------【甜味】馒头制作开始------
准备食材:发面...
面粉中加糖,调甜味...
和面...
蒸馒头,出锅...
------【甜味】馒头制作结束------
------小鸟造型【普通】馒头制作开始------
准备食材:发面...
和面...
和面后,开始给馒头做成小鸟造型...
蒸馒头,出锅...
------小鸟造型【普通】馒头制作结束------
------小鸟造型【甜味】馒头制作开始------
准备食材:发面...
面粉中加糖,调甜味...
和面...
和面后,开始给馒头做成小鸟造型...
蒸馒头,出锅...
------小鸟造型【甜味】馒头制作结束------

请大家仔细看测试类中,最后制作小鸟造型甜味馒头的方法,是制作小鸟馒头的类,持有了一个制作甜味馒头的对象,基于甜味馒头,加上小鸟馒头的制作方法,就制作出了小鸟造型甜味馒头

也就是做,我们可以认意组合各种馒头的对象,这样就能做出各种各样的馒头,这就是装饰器模式的威力。这就好像是积木一样,基于几块基本的木块,可以让你摆出各种造型。

类结构图:

总结:装饰器模式意在对一个对象的功能进行增强,通过持有一个对象,来增强其功能。我们demo中,继承了普通馒头,就是为了能让各种馒头都作为对象,传入到另一种馒头制作类中,从而实现各种组合和装饰效果。