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