装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式作为现有类的一个包装。
概念
装饰器模式涉及四个角色:
- 组件(Component):定义一个对象接口,可以给这些对象动态地添加职责。
- 具体组件(Concrete Component):定义了一个具体的对象,也可以给这个对象添加一些职责。
- 装饰(Decorator):持有一个组件(Component)对象的实例,并定义了与组件接口一致的接口。
- 具体装饰(Concrete Decorator):负责给组件添加额外的职责。
优点
- 增加对象的职责:装饰器模式提供了一种灵活的替代方案来扩展对象的功能,比继承更加灵活。
- 动态地添加功能:装饰器模式允许用户动态地给一个对象添加额外的职责。添加的职责也容易撤销。
- 扩展系统功能:可以使用多个不同的装饰器对同一个对象进行装饰,实现不同的效果。
缺点
- 会导致系统产生很多小对象:每个装饰器都是一个对象,过多使用装饰器会造成程序中小对象的数量大增。
- 复杂性增加:装饰器模式会增加系统的复杂性,多层装饰比较复杂。
- 维护困难:大量使用装饰器,维护时需要特别注意装饰链的配置,可能会引起错乱。
Java代码示例
考虑一个简单的咖啡店系统,咖啡是一种饮料,顾客可以选择添加不同的调料(如牛奶、摩卡、豆浆等)。
首先,定义组件接口:
java复制代码
public interface Beverage {
String getDescription();
double cost();
}
具体组件实现:
java复制代码
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
public class HouseBlend implements Beverage {
@Override
public String getDescription() {
return "House Blend Coffee";
}
@Override
public double cost() {
return 0.89;
}
}
装饰器实现:
java复制代码
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public abstract String getDescription();
}
具体装饰实现:
java复制代码
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return .20 + beverage.cost();
}
}
public class Soy extends CondimentDecorator {
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
return .15 + beverage.cost();
}
}
使用示例:
java复制代码
public class DecoratorPatternDemo {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new HouseBlend();
beverage2 = new Mocha(beverage2);
beverage2 = new Soy(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
在这个例子中,Espresso
和HouseBlend
是具体的饮料,Mocha
和Soy
是装饰者,它们通过包装一个Beverage
对象并在其基础上添加额外的功能(即调料)来工作。