装饰者模式
装饰者模式(Decorator),不改变原本对象,动态地给一个对象添加一些额外的职责,比继承更加灵活(继承在扩展功能上是静态的),符合开闭原则(对于扩展是开放的,对于更改是封闭的)
Component:抽象的装饰主体
ConcreteComponent:具体的装饰主体
Decorator:抽象的装饰者
ConcreteDecorator:具体的装饰者
结构图:
场景:
假如现在有饮料需要加配料出售,饮料分为纯牛奶和纯净水,配料有糖、茶,如果使用子类继承的方式(饮料都加配料)实现价格计算,将需要六种方案,也就是需要写六个子类来实现。如果业务扩展,饮料种类增加,配料种类也增加,将会产生大量的子类,即“类爆炸”。
解决:
采用装饰者模式将可以解决这个问题。被装饰的是饮料本身(纯牛奶、纯净水),配料的不同选择即为不同的装饰。
结构图:
例:
public interface Drink { // Component float cost(); String desc(); }
public class Milk implements Drink { // ConcreteComponent1 @Override public float cost() { return 10.0f; } @Override public String desc() { return "纯牛奶"; } }
public class Water implements Drink { // ConcreteComponent2 @Override public float cost() { return 1.0f; } @Override public String desc() { return "纯净水"; } }
public abstract class DrinkDecorator implements Drink { // Decorator private Drink drink; // 要装饰的对象 public Decorator(Drink drink) { this.drink = drink; } @Override public float cost() { return drink.cost(); } @Override public String desc() { return drink.desc(); } }
public class SugarDecorator extends DrinkDecorator{ public SugarDecorator(Drink drink) { super(drink); } @Override public float cost() { return super.cost() + 1.0f; } @Override public String desc() { return super.desc() + "+糖"; } }
public class TeaDecorator extends DrinkDecorator{ public TeaDecorator(Drink drink) { super(drink); } @Override public float cost() { return super.cost() + 2.0f; } @Override public String desc() { return super.desc() + "+茶"; } }
public class Test { public static void main(String[] args) { System.out.println("装饰方式一:"); // 主料:纯牛奶,先加糖后加茶 Drink milk = new Milk(); SugarDecorator sugar = new SugarDecorator(milk); // 纯牛奶 + 糖(第一步装饰) TeaDecorator tea = new TeaDecorator(sugar); // 纯牛奶 + 糖 + 茶(第二步装饰) System.out.println(tea.desc()); System.out.println(tea.cost()); System.out.println("装饰方式二:"); // 主料:纯净水,先加茶后加水 Drink water = new Water(); TeaDecorator tea1 = new TeaDecorator(water); // 纯净水 + 茶(第一步装饰) SugarDecorator sugar1 = new SugarDecorator(tea1); // 纯净水 + 茶 + 糖(第二步装饰) System.out.println(sugar1.desc()); System.out.println(sugar1.cost()); } }
输出:
装饰方式一: 纯牛奶+糖+茶 13.0 装饰方式二: 纯净水+茶+糖 4.0
说明:
根据面向接口编程的思想,Drink(角色:Component)需是接口,Milk、Water(角色:ConcreteComponent)是其具体实现类,DrinkDecorator(角色:Decorator)也需是其实现类,SugarDecorator、TeaDecorator(角色:ConcreteDecorator)是DrinkDecorator的子类。
分析:
Drink milk = new Milk();
SugarDecorator sugar = new SugarDecorator(milk); // 1.纯牛奶 + 糖(第一步装饰)
TeaDecorator tea = new TeaDecorator(sugar); // 2.纯牛奶 + 糖 + 茶(第二步装饰)
首先创建装饰主体:Milk类对象milk,然后利用SugarDecorator的构造器对其进行装饰,随后再将SugarDecorator装饰过的对象传入TeaDecorator的构造器对其进行再次装饰。
看第2处代码,再次装饰时,构造器需传入Drink的实现类对象,所以已被装饰过的对象必须是Drink接口的实现类,而已被装饰过的对象是DrinkDecorator的子类,所以DrinkDecorator(Decorator)需实现Drink接口(Component)。
如果,饮料只有一种(纯牛奶),则不需要Drink接口(Component),直接将DrinkDecorator(Decorator)继承Milk(ConcreteComponent)即可。结构图如下:
而且,Decorator也可以有多个,完成不同场景下的装饰工作。