定义
装饰者模式又叫包装(Wrapper)模式。装饰者模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰者模式动态地将责任附加到对象身上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
在装饰模式中的角色:
- 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
优缺点
优点:
-
相比继承关系提供更多的灵活性
-
通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
-
类数量容易爆炸
-
会使系统变复杂
实例
需求:
小明要买一个山东煎饼,煎饼里面可以填充很多东西,如香肠、油条、里脊,小明每天加的料都不同。请根据以上条件做一个价格计算程序。
添加一个饼接口:
/**
* 饼接口
*/
public interface IPancake {
String getDesc();
BigDecimal getPrice();
}
实现一个山东煎饼:
/**
* 山东煎饼
*/
public class ShandongPancake implements IPancake {
@Override
public String getDesc() {
return "山东煎饼";
}
@Override
public BigDecimal getPrice() {
return new BigDecimal(4);
}
}
添加一个饼配料接口:
/**
* 饼配料接口
*/
public interface ICakeIngredients extends IPancake{
}
实现饼配料:
public class Egg implements ICakeIngredients {
private IPancake pancake;
public Egg(IPancake pancake){
this.pancake = pancake;
}
@Override
public String getDesc() {
return pancake.getDesc() +" + 鸡蛋";
}
@Override
public BigDecimal getPrice() {
return pancake.getPrice().add(new BigDecimal(1));
}
}
public class Sausage implements ICakeIngredients {
private IPancake pancake;
public Sausage(IPancake pancake){
this.pancake = pancake;
}
@Override
public String getDesc() {
return pancake.getDesc() +" + 香肠";
}
@Override
public BigDecimal getPrice() {
return pancake.getPrice().add(new BigDecimal(1.5));
}
}
public class Fritters implements ICakeIngredients {
private IPancake pancake;
public Fritters(IPancake pancake){
this.pancake = pancake;
}
@Override
public String getDesc() {
return pancake.getDesc() +" + 油条";
}
@Override
public BigDecimal getPrice() {
return pancake.getPrice().add(new BigDecimal(2));
}
}
接下来我们来模拟下购买场景:
public static void main(String[] args) {
// 普通山东煎饼
IPancake ordinaryShandongPancake = new ShandongPancake();
System.out.println(String.format("%s ¥%s", ordinaryShandongPancake.getDesc(),
ordinaryShandongPancake.getPrice()));
// 加料山东煎饼
IPancake feedingShandongPancake = new ShandongPancake();
// 加一个鸡蛋
feedingShandongPancake = new Egg(feedingShandongPancake);
// 加一个香肠
feedingShandongPancake = new Sausage(feedingShandongPancake);
// 加一个油条
feedingShandongPancake = new Fritters(feedingShandongPancake);
// 再加一个鸡蛋
feedingShandongPancake = new Egg(feedingShandongPancake);
System.out.println(String.format("%s ¥%s", feedingShandongPancake.getDesc(),
feedingShandongPancake.getPrice()));
}
控制台输出:
山东煎饼 ¥4
山东煎饼 + 鸡蛋 + 香肠 + 油条 + 鸡蛋 ¥9.5