装饰者模式
装饰者模式的定义
- 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者模式是一种“结构型”模式。
装饰者模式的使用场景
- 需要透明且动态地扩展类的功能时。
装饰者UML类图
角色介绍
Component:抽象组件。
可以是一个接口或抽象类,其充当的就是被装饰的原始对象。ConcreteComponent: 组件具体实现类。
该类是Component类的基本实现,也是我们装饰的具体对象。Decorator: 抽象装饰者。
顾名思义,其承担的职责就是为了装饰我们的组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然,如果装饰逻辑单一,只有一个的情况下我们可以省略该类直接作为具体的装饰者。ConcreteDecoratorA: 装饰者具体实现类。
只是对抽象装饰者作出具体的实现。ConcreteDecoratorB: 同上。
同上。
例子: HeadFirst一书中 星巴兹饮料例子
星巴兹饮料装饰者模式类图:
具体代码实现如下:
饮料抽象类:
public abstract class Beverage {
protected String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
黑色烘焙饮料:
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
}
@Override
public double cost() {
return .99;
}
}
脱咖啡因:
public class Decaf extends Beverage {
public Decaf() {
description = "Decaf Coffee";
}
@Override
public double cost() {
return 1.05;
}
}
浓咖啡:
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
混合咖啡:
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
@Override
public double cost() {
return .89;
}
}
调味品装饰者抽象类:
public abstract class CondimentDecorator extends Beverage {
@Override
public abstract String getDescription();
}
具体装饰者:牛奶
public class Milk extends CondimentDecorator {
private Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
@Override
public double cost() {
return .10 + beverage.cost();
}
}
具体装饰者:摩卡
public class Mocha extends CondimentDecorator {
private Beverage beverage;
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return .20 + beverage.cost();
}
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
具体装饰者:豆浆
public class Soy extends CondimentDecorator {
private Beverage beverage;
@Override
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
return .15 + beverage.cost();
}
public Soy(Beverage beverage) {
this.beverage = beverage;
}
}
具体装饰者:奶泡
public class Whip extends CondimentDecorator {
private Beverage beverage;
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return 0.10 + beverage.cost();
}
public Whip(Beverage beverage) {
this.beverage = beverage;
}
}
测试下我们的程序
public class StarbuzzCoffee {
public static void main(String[] args){
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
// 制造 DarkRoast(双倍摩卡奶泡咖啡)
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2); // 使用 mocha 装饰它
beverage2 = new Mocha(beverage2); // 使用第二个 mocha 装饰它
beverage2 = new Whip(beverage2); // 使用 whip 装饰它
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
}
}
测试结果:
Espresso 1.49
House Blend Coffee, Soy, Mocha, Whip $1.34
总结
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方
式。 - 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
- 组合和委托可用于在运行时动态地加上新的行为。
- 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
- 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
- 你可以用无数个装饰者包装一个组件。
- 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
优点
- 除了继承,装饰者模式也可以让我们扩展行为。
- 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
缺点
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
Android源码中的装饰者模式实现
- ContextWrapper