什么是装饰器模式
按照单一职责原则,某一个对象只专注于干一件事,而如果要扩展其职能的话,不如想办法分离出一个类来“包装”这个对象,而这个扩展出的类则专注于实现扩展功能。
装饰器模式就可以将新功能动态地附加于现有对象而不改变现有对象的功能。
使用场景:
- 运行时,你需要动态地为对象增加额外职责时;
- 当你需要一个能够代替子类的类,借助它提供额外方法时。
装饰器模式代码实现
假设我去买咖啡,首先服务员给我冲了一杯原味咖啡,我希望服务员给我加些牛奶和白糖混合入原味咖啡中。使用装饰器模式就可以解决这个问题。
咖啡接口,定义了获取花费和配料的接口
/**
* 咖啡
*/
interface Coffee {
/** 获取价格 */
double getCost();
/** 获取配料 */
String getIngredients();
}
原味咖啡,实现Coffe接口,花费1元,配料中,只有咖啡
/**
* 原味咖啡
*/
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1;
}
@Override
public String getIngredients() {
return "Coffee";
}
}
咖啡对象的装饰器类,同样实现Coffee接口,定义一个Coffe对象的引用,在构造器中进行初始化。并且将getCost()和getIntegredients()方法转发给被装饰对象。
/**
* 咖啡的"装饰器",可以给咖啡添加各种"配料"
*/
abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
/**
* 在构造方法中,初始化咖啡对象的引用
*/
public CoffeeDecorator(Coffee coffee) {
decoratedCoffee = coffee;
}
/**
* 装饰器父类中直接转发"请求"至引用对象
*/
public double getCost() {
return decoratedCoffee.getCost();
}
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
具体的装饰器类,负责往咖啡中“添加”牛奶,注意看getCost()方法和getIngredients()方法,可以在转发请求之前或者之后,增加功能。如果是代理模式,这里的结构就有所不同,通常代理模式根据运行时的条件来判断是否转发请求。
/**
* 此装饰类混合"牛奶"到原味咖啡中
*/
class WithMilk extends CoffeeDecorator {
public WithMilk(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
double additionalCost = 0.5;
return super.getCost() + additionalCost;
}
@Override
public String getIngredients() {
String additionalIngredient = "milk";
return super.getIngredients() + ", " + additionalIngredient;
}
}
另一个具体装饰器类,用来给咖啡加糖,一样的逻辑。
class WithSugar extends CoffeeDecorator {
public WithSugar(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 1;
}
@Override
public String getIngredients() {
return super.getIngredients() + ", Sugar";
}
}
客户端使用装饰器模式
public class DecoratorDemo {
static void print(Coffee c) {
System.out.println("花费了: " + c.getCost());
System.out.println("配料: " + c.getIngredients());
System.out.println("============");
}
public static void main(String[] args) {
//原味咖啡
Coffee c = new SimpleCoffee();
print(c);
//增加牛奶的咖啡
c = new WithMilk(c);
print(c);
//再加一点糖
c = new WithSugar(c);
print(c);
}
}
输出结果L:
花费了: 1.0
配料: Coffee
============
花费了: 1.5
配料: Coffee, milk
============
花费了: 2.5
配料: Coffee, milk, Sugar
============
从上个例子可以看出,装饰器模式的结构很像代理模式,装饰器模式的请求转发过程很像职责炼模式,只不过:
职责链模式在转发请求过程中,最多只有一个对象会处理请求,而装饰器模式则有多个对象处一个请求。
装饰器模式是代替增加子类的一种解决方案,体现了聚合/合成复用原则的思想,尽量使用组合的方式来扩展功能,这样就把基本功能和扩展功能解耦了,使得代码可复用,可维护,灵活。关键点在于装饰器模式可以动态地为对象增加扩展功能。