Java设计模式之装饰者模式

装饰者模式,通过这种设计模式为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在,例如:
java.io.BufferedInputStream
java.io.FileReader

这些类够熟悉吧,理解了装饰者模式就能更好地理解Java的IO体系,今天我们结合《Head First 设计模式》的例子来学习一下装饰者模式是怎么一回事。

装饰者模式的前世

先看看为什么需要这一模式。
原来我们要拓展新功能都是通过继承父类去产生新的子类。就拿书上的咖啡店举例子,饮料是基类,饮料各式各样,名称做法不一,就会拓展出一堆子类,随着客户需求增多,饮料的品种也会增多,如果我们一个新品种一个子类的话,回过头去看系统,我们会发现简直爆炸,有很多类需要我们去维护。一旦原料价格有变化或者饮料配方需要改进,我们就不得不去修改原有的类。这种做法违背了我们面向对象设计原则之一:类应该对扩展开放,对修改关闭。原有的代码可能经过多次测试确定功能无误,但你一旦修改了原来的代码,就不能保证原来的确定性了,所以我们应该尽可能拒绝这种做法。基于以上的分析,对于饮料,我们可以这样设计,最基本的饮料,然后配上(装饰)想要的配料,再委托计算出新饮料的价格(配料价格+原来的价格)。我们看看书上对装饰者模式的定义:
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
类图如图所示:
Java设计模式之装饰者模式_第1张图片
我们可以看到ConcreteComponent和Decorator都用到继承,这样做的是要保证装饰者和被装饰者都要是同一类型,我们利用继承来保证类型匹配,而不是利用继承来获得行为。那行为从哪来?装饰者和组件组合在一起,就产生新的行为。利用组合还有一个好处就是运行时动态扩展。下来我们直接看用代码是怎么实现的。

装饰者模式的今生

借用书上的例子,自己敲一遍。
(1)饮料基类

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public abstract class Beverage {
    String desc = "UnKnown Beverage";

    public String getDesc() {
        return desc;
    }

    public abstract double cost();
}

(2)调料基类

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public abstract class CondimentDecorator extends Beverage{
    public abstract String getDesc();
}

(3)普通咖啡

package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public class Espresso extends Beverage {

    public Espresso(){
        desc = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}

(4)调料(牛奶和摩卡)

package Decorator;

/**
 * Created by gray on 2017/9/24.
 */
public class Milk extends CondimentDecorator {
    Beverage beverage;
    public Milk(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDesc() {
        return beverage.getDesc() + ", Milk";
    }

    @Override
    public double cost() {
        return 0.50 + beverage.cost();
    }
}
package Decorator;

/**
 * Created by gray on 2017/9/23.
 */
public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDesc() {
        return beverage.getDesc() + ", Mocha";
    }

    @Override
    public double cost() {
        return 0.20 + beverage.cost();
    }
}

(5) 最后是测试代码和测试结果

package Decorator;

/**
 * Created by gray on 2017/9/24.
 */
public class Test {
    public static void main(String args[]){
        Beverage beverage = new Espresso();
        System.out.println("no Condiment : "+ beverage.getDesc() + " | total : $" + beverage.cost());

        Beverage beverage1 = new Mocha(new Espresso());
        System.out.println("have Mocha : " + beverage1.getDesc() + " | total : $" + beverage1.cost());

        Beverage beverage2 = new Milk(new Mocha(new Espresso()));
        System.out.println("have Milk,Mocha : " + beverage2.getDesc() + " | total : $" + beverage2.cost());
    }
}

这里写图片描述
总结:装饰者模式要说的东西不是很多,弄清楚装饰者模式再去看jdk中的I/O的相关类就显得简单了,里面绝大多数类都是装饰者。装饰者模式虽然比继承多了些灵活性,但同时也不可避免地出现许多小类,增加了API的复杂性,所有得看场景,当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰者模式;还有就是如上面分析所说的,子类会爆炸性增长地时候就要考虑使用装饰者模式。

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