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方法不需要不改变