装饰器模式
(文章目录)
什么是装饰器模式
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
为什么使用装饰器模式
使用装饰者模式有如下好处: 1. 避免了类爆炸问题,简化了设计 2. 易于扩展,可以灵活组合不同调料 3. 装饰类和被装饰类可以独立变化,没有严格耦合 4. 符合开闭原则,扩展开放(可以添加更多调料),修改关闭(原有类不需要修改) 我们举个例子来说明,假设我们要开发一个咖啡店点餐系统。我们有以下对象:
- Coffee:咖啡类,价格5元
- Milk:牛奶类,价格1元
- ChocolateChip:巧克力豆类,价格2元
- WhipCream:奶油类,价格2元
如果不使用装饰者模式,我们可以这样设计:
// 咖啡类
public class Coffee {
private String description = "咖啡";
private double price = 5;
}
// 牛奶咖啡
public class CoffeeWithMilk extends Coffee {
public CoffeeWithMilk() {
description += " + 牛奶";
price += 1;
}
}
// 巧克力豆咖啡
public class CoffeeWithChocolateChip extends Coffee {
// ...
}
// 奶油咖啡
public class CoffeeWithWhipCream extends Coffee {
// ...
}
// 巧克力豆牛奶咖啡
public class CoffeeWithMilkAndChocolateChip extends CoffeeWithMilk {
// ...
}
但是这种设计有几个缺点:
- 会产生很多子类,导致过度继承,设计变得复杂
- 如果想添加更多的调料组合,子类数量会急剧增加,不易于维护
- Coffee 类和子类是耦合的,一旦 Coffee 类变化,子类也需要变化
使用装饰者模式可以这样设计:
java
public interface Coffee {
public double getPrice();
}
public class SimpleCoffee implements Coffee {
@Override
public double getPrice() {
return 5;
}
}
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double getPrice() {
return coffee.getPrice();
}
}
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}
@Override
public double getPrice() {
return super.getPrice() + 1;
}
}
// 其他调料装饰类(ChocolateChip,WhipCream)定义类似...
Coffee c = new SimpleCoffee();
c = new Milk(c); // 牛奶
c = new ChocolateChip(c);// 牛奶巧克力豆
c = new WhipCream(c);// 牛奶巧克力豆
System.out.println(c.getPrice()); // 9
如何使用装饰器模式
从上面的代码中,我们可以看出,装饰器模式主要包含以下角色。
1、抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。 2、具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。 3、抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。 4、具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
上面的代码示例中,我们将各个类进行下划分:
- Coffee 接口属于抽象构件
- SimpleCoffee 类属于具体构件
- CoffeeDecorator 抽象类属于抽象装饰
- Milk、ChocolateChip 等类属于具体装饰
总结
装饰器模式不是简单的“用组合替代继承”,装饰器模式相对于简单的组合关系,还有两个比较特殊的地方。
- 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
- 装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。