装饰者模式

装饰者模式

装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例

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

设计原则

  • 类应该对扩展开放,对修改关闭。

案例

这里我们采用一个制作饮料的过程来介绍装饰者模式,我们先来分析一下在饮料制作的过程中有哪些东西是经常变动的,比如饮料是在牛奶里面加巧克力,蜂蜜等等,也有可能是往红茶中添加柠檬之类的,诸如此类,这样如果我们在设计基础饮料时添加太多的配料属性的话,后面如果我们想要更灵活地添加新的配料就很麻烦了。

我们采用装饰者模式,所有的如牛奶,红茶等等这一类主原料,全部继承于一个抽象的饮料类,然后所有的配料再继承于一个抽象的配料类,而抽象的配料类也同时继承抽象的饮料类。这里的主原料则是一个被装饰者,而配料则是装饰者。

例如:主原料的基础抽象类Beverage(饮料)

/**
 * 饮料类
 * 装饰者模式中的被装饰者基类
 *
 */
public abstract class Beverage {

    String description = "Unknown Beverage";
    public String getDescription() {
        return description;
    }
    public abstract double cost();
}

然后是配料类的抽象类CondimentDecorator,注意装饰者类需要继承被装饰者的基类或接口

/**
 * 装饰者模式中的装饰者的抽象基类
 *
 */
public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

一个被装饰者类

/**
 * 具体的被装饰者实现类
 * 浓缩咖啡Espresso
 *
 */
public class Espresso extends Beverage {

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

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

}

被装饰者类

/**
 * 具体的被装饰者实现类
 *
 */
public class HouseBlend extends Beverage {


    public HouseBlend() {
        description = "House Blend Coffe";
    }

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

}

具体的装饰者类,注意这里继承了装饰者的基类,而这个基类也继承了被装饰者基类,故装饰者类需要继承被装饰者类或实现接口;并且在装饰者类中需要持有被装饰者对象实例。

/**
 * 装饰者类
 * 摩卡
 *
 */
public class Mocha extends CondimentDecorator {
    Beverage beverage;

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

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

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

}

/**
 * 装饰类
 * 巧克力
 * @author 37111
 *
 */
public class Chocolate extends CondimentDecorator {
    Beverage beverage;

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

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",chocolate";
    }

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

}

测试:

public class Test {

    public static void main(String[] args) {
        Beverage b1 = new Espresso();
        b1 = new Mocha(b1);
        b1 = new Chocolate(b1);
        System.out.println(b1.getDescription() + "$" + b1.cost());
        Beverage b2 = new HouseBlend();
        b2 = new Chocolate(b2);
        System.out.println(b2.getDescription() + "$" + b2.cost());
    }

}

结果:

Espresso, Mocha,chocolate$2.69
House Blend Coffe,chocolate$1.3

这里我们可以看到使用装饰者模式的好处,当需要添加更多的配料时,只需要再写相应的继承了被装饰者基类的配料类,并且当我们修改任意的配料类的时候,所有的结果也就跟着一起修改了,这样也就实现了类之间的解耦合。

实际中的应用

在java的API中使用到装饰者模式的地方有很多,java的I/O流中就使用了装饰者模式BufferedInputStream及LineNumberInputStream都扩展自FilterInputStream,而FilterInputStream是一个抽象的装饰类。

-------------本文结束感谢您的阅读-------------