Java设计模式——装饰者模式

Java设计模式之装饰者模式

前面两篇文章介绍了四个设计模式的原则:

  • 把变化的抽取出来,不要和不变化的放在一起
  • 针对接口编程,不针对实现编程
  • 多用组合,少用继承
  • 为了交互对象之间的松耦合的设计而努力。

现在我们要介绍最重要的设计原则:

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

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

可以动态地给对象添加一些额外的属性或者行为

装饰者模式:

  • 装饰者和被装饰者有相同的超类
  • 可以用一个或者多个装饰者包装一个对象
  • 因为装饰者和被装饰者有相同的超类,所以在任何需要原始对象(被包装的)的时候,你都可以用装饰过的对象替代。
  • 装饰者可以在被装饰者的行为前后添加自己的行为。
  • 装饰者可以如何时候被装饰,可以运行时动态地装饰对象。

Head First 设计模式中案例

星巴克做的咖啡有很多种类,我们想在这些咖啡上添加一些原料(牛奶、豆浆、奶泡、摩卡),还有咖啡的规格Size(大中小),我们决定使用装饰者模式来完成

  • 装饰者就是具体的原料类

  • 被装饰者就是具体咖啡类

我们使用一个抽象的Beverage类描述星巴克的咖啡(当然也可以使用接口),各个不同种类的咖啡继承该抽象类,我们还需要一个抽象的装饰者类,继承该装饰者类实现具体的原料装饰类,根据装饰者模式的被装饰者和装饰者用相同的超类,我们的装饰者抽象类需要继承Beverage

计算价格我们要调用最外层的cost方法,每一层会委托它装饰的对象-被装饰者计算出价格

当我们最外层装饰者为奶泡Whip时,它会委托给上一层装饰者摩卡Mocha计算出摩卡所装饰的具体咖啡的价格+摩卡的价格;然后再加上自己奶泡的价格。(之所以可以这样实现调用多层cost是因为他们都来自一个超类对象)

Beverage类:

public abstract class Beverage {
    //规格
    protected Size size = Size.UNKNOWN;
    public void setSize(Size size){
        this.size = size;
    }
    //描述
    protected String description = "unKnown Beverage";
    public String getDescription(){
        return description;
    }
    //价格
    public abstract double cost();
}

我们使用枚举类定义Size:

  • 小杯+0.1元
  • 中杯+0.15元
  • 大杯+0.2元
public enum Size {
    SMALL(0.10)
    ,MEDIUM(0.15)
    ,BIG(0.20)
    ,UNKNOWN(0);
    private double cost;
    Size(double v) {
        cost = v;
    }
    public double getCost(){return cost;}
}

三个具体的咖啡类:

/**
 * 深培咖啡
 */
public class DarkRoast extends Beverage{
    //构造时传入规格
    public DarkRoast(Size size){
        setSize(size);
        if(this.size==Size.UNKNOWN){
            throw new RuntimeException();
        }
        description = "DarkRoast "+this.size.name();
    }
//30.00元+规格需加的钱
    public double cost() {
        return 30.00+size.getCost();
    }
}
/**
 *浓缩咖啡
 */
public class Espresso extends Beverage{
    public Espresso(Size size){
        setSize(size);
        if(this.size==Size.UNKNOWN){
            throw new RuntimeException();
        }
        description = "Espresso "+this.size.name();
    }
    public double cost() {
        return 25.00+size.getCost();
    }
}
/**
 * 混合咖啡
 */
public class HouseBlend extends Beverage{
    public HouseBlend(Size size){
        setSize(size);
        if(this.size==Size.UNKNOWN){
            throw new RuntimeException();
        }
        description = "HouseBlend "+this.size.name();
    }
    public double cost() {
        return 50.00+size.getCost();
    }
}

原料装饰者抽象类:

public abstract class CondimentDecorator extends Beverage {
    //描述中需要有原料信息,所以需要重写该抽象方法
    public abstract String getDescription();
}

四个原料装饰者类:

  • 牛奶、摩卡、豆浆、奶泡
public class Milk extends CondimentDecorator{
    private Beverage beverage;
    //构造方法传入需要装饰的咖啡
    public Milk(Beverage beverage){
        this.beverage = beverage;
    }
    //牛奶需要多加9元
    public double cost() {
        return beverage.cost()+9.00;
    }
    //重写的描述放方法
    public String getDescription() {
        return beverage.getDescription()+" with Milk";
    }
}
public class Mocha extends CondimentDecorator{
    private Beverage beverage;
    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }
    public double cost() {
        return beverage.cost()+7.00;
    }
    public String getDescription() {
        return beverage.getDescription()+" with Mocha";
    }
}
public class Soy extends CondimentDecorator{
    private Beverage beverage;
    public Soy(Beverage beverage){
        this.beverage = beverage;
    }
    public double cost() {
        return beverage.cost()+5.00;
    }
    public String getDescription() {
        return beverage.getDescription()+" with Soy";
    }
}
public class Whip extends CondimentDecorator{
    private Beverage beverage;
    public Whip(Beverage beverage){
        this.beverage = beverage;
    }
    public double cost() {
        return beverage.cost()+15.00;
    }
    public String getDescription() {
        return beverage.getDescription()+" with Whip";
    }
}

编写测试类:

public class Main {
    public static void main(String[] args) {
        DarkRoast darkRoast = new DarkRoast(Size.BIG);
        Milk mb = new Milk(darkRoast);
        System.out.println(mb.getDescription()+",cost:"+mb.cost());

        Mocha mochab = new Mocha(new Espresso(Size.SMALL));
        System.out.println(mochab.getDescription()+",cost:"+mochab.cost());

        Soy sb = new Soy(new HouseBlend(Size.MEDIUM));
        System.out.println(sb.getDescription()+",cost:"+sb.cost());

        Whip wb = new Whip(new DarkRoast(Size.BIG));
        System.out.println(wb.getDescription()+",cost:"+sb.cost());

        Milk mb2 = new Milk(mb);
        System.out.println(mb2.getDescription()+",cost:"+mb2.cost());

        Soy soyb = new Soy(mb2);
        System.out.println(soyb.getDescription()+",cost:"+soyb.cost());

        Whip whipb = new Whip(soyb);
        System.out.println(whipb.getDescription()+",cost:"+whipb.cost());
    }
}

输出如下

output:
DarkRoast BIG with Milk,cost:39.2
Espresso SMALL with Mocha,cost:32.1
HouseBlend MEDIUM with Soy,cost:55.15
DarkRoast BIG with Whip,cost:55.15
DarkRoast BIG with Milk with Milk,cost:48.2
DarkRoast BIG with Milk with Milk with Soy,cost:53.2
DarkRoast BIG with Milk with Milk with Soy with Whip,cost:68.2

JDK中装饰者——IO

IO中InputStreamOutputStream就是抽象的超类,ReaderWriter也是抽象的超类。整个IO家族都是由装饰者模式构成的。

代码地址:Github装饰者模式


Java设计模式|策略模式

Java设计模式|观察者模式

Java设计模式|装饰者模式

Java设计模式|工厂模式

Java设计模式|命令模式

Java设计模式|适配器模式和外观模式

Java设计模式|模板方法模式

Java设计模式|迭代器模式和组合模式

Java设计模式|状态模式

Java设计模式|代理模式

Java设计模式|单例模式

Java设计模式|备忘录模式

Java设计模式|访问者模式

Java设计模式|复合模式

Java设计模式|桥接模式

Java设计模式|生成器模式

Java设计模式|享元模式/蝇量模式

Java设计模式|原型模式

Java设计模式|责任链模式

Java设计模式|中介者模式

  • 本文作者: dzou | 微信:17856530567
  • 本文链接: http://www.dzou.top/post/1b3210b0.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!