1 简介
  装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
2 特点
从角色上来说,可分为装饰者(装饰对象)和被装饰者
    1) 装饰对象和被装饰者有相同的接口。
         2) 装饰对象包含一个被装饰者的引用(reference)
         3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给被装饰者。
         4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展
3 示例
3.1 需求
有这么一个需求,有不同品种的咖啡,有不同的配料,咖啡和配料可以随意组合,算出他们的价格。要求,具有良好的扩展性,咖啡和配料的种类随时可以扩展
3.2 设计
1)一个抽象类Drink,抽象出咖啡和配料的公有部分:两个变量:价格和描述。一个获取价格方法:cost
2)咖啡类继承Drink,实现cost方法
3)配料类继承Drink,实现cost方法,并且在里面加了一个参数drink,用来放coffee
这里的关键是咖啡和配料继承同一个父类,并且在配料里面增加了一个放Drink参数,这样每次创建配料就把coffee传进去,就获得了咖啡+配料

3.3 代码
3.3.1 Drink
public abstract class Drink {
    Double price = 0.0;
    String desc = "";
    abstract Double cost();
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
}
3.3.2 Coffee
public class Coffee extends Drink{
    @Override
    public Double cost() {
        return super.getPrice();
    }
}
3.3.3 BlackCoffee
public class BlackCoffee extends Coffee {
    public BlackCoffee() {
        //初始化价格和描述
        setPrice(4.0);
        setDesc("黑咖啡");
    }
}
3.3.4 Cappuccino
public class Cappuccino extends Coffee{
    public Cappuccino() {
        setPrice(6.0);
        setDesc("卡布奇洛");
    }
}
3.3.5 Seaoning
public class Seasoning extends Drink{
    //把Drink对象放在调料的类那里,每次创建调料放入coffee
    public Drink drink;
    public Drink getDrink() {
        return drink;
    }
    public void setDrink(Drink drink) {
        this.drink = drink;
    }
    public Double cost() {
        return super.getPrice() + drink.cost();
    }
    public String getDesc() {
        return super.getDesc()+ "+" + drink.getDesc();
    }
    public Seasoning(Drink drink) {
        this.drink = drink;
    }
    
}
3.3.6 Milk
public class Milk extends Seasoning {
    
    public Milk(Drink drink) {
        super(drink);
        setPrice(2.0);
        setDesc("牛奶");
    }
}
3.3.7 Sugar
public class Sugar extends Seasoning{
    public Sugar(Drink drink) {
        super(drink);
        setPrice(1.0);
        setDesc("糖");
    }
}
3.3.8 测试
public class DrinkMaker {
    public static void main(String[] args) {
        //单品黑咖啡
        Drink d = new BlackCoffee();
        System.out.println(d.cost());
        System.out.println(d.getDesc());
        //加牛奶
        d = new Milk(d);
        System.out.println(d.cost());
        System.out.println(d.getDesc());
        //加糖
        d = new Sugar(d);
        System.out.println(d.cost());
        System.out.println(d.getDesc());
    }
}
执行结果
4.0
黑咖啡
6.0
牛奶+黑咖啡
7.0
糖+牛奶+黑咖啡
3.4 扩展
增加一种配料,只需要继承Seaoning即可
增加一种coffee,只需要继承Coffee即可
cost方法不需要不改变