装饰器模式
基础概念
什么是装饰器模式?
答案: 装饰器模式(Decorator Pattern)动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
使用场景:
- 需要扩展一个类的功能
- 动态地给对象添加功能
- 需要为一批类增加功能
优点:
- 比继承更灵活
- 符合开闭原则
- 可以动态组合
缺点:
- 会产生很多小对象
- 多层装饰比较复杂
实现方式
基本实现?
答案:
java
// 组件接口
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("基本功能");
}
}
// 装饰器抽象类
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器A
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunction();
}
private void addedFunction() {
System.out.println("装饰器A的附加功能");
}
}
// 具体装饰器B
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunction();
}
private void addedFunction() {
System.out.println("装饰器B的附加功能");
}
}
// 使用
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.operation();
// 输出:
// 基本功能
// 装饰器A的附加功能
// 装饰器B的附加功能实际应用
咖啡订单系统
答案:
java
// 饮料接口
public interface Beverage {
String getDescription();
double cost();
}
// 具体饮料:浓缩咖啡
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "浓缩咖啡";
}
@Override
public double cost() {
return 25.0;
}
}
// 具体饮料:美式咖啡
public class Americano implements Beverage {
@Override
public String getDescription() {
return "美式咖啡";
}
@Override
public double cost() {
return 20.0;
}
}
// 调料装饰器
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
// 牛奶装饰器
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 牛奶";
}
@Override
public double cost() {
return beverage.cost() + 5.0;
}
}
// 摩卡装饰器
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 摩卡";
}
@Override
public double cost() {
return beverage.cost() + 8.0;
}
}
// 使用
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " ¥" + beverage.cost());
beverage = new Milk(beverage);
System.out.println(beverage.getDescription() + " ¥" + beverage.cost());
beverage = new Mocha(beverage);
System.out.println(beverage.getDescription() + " ¥" + beverage.cost());
// 输出:
// 浓缩咖啡 ¥25.0
// 浓缩咖啡, 牛奶 ¥30.0
// 浓缩咖啡, 牛奶, 摩卡 ¥38.0Java IO中的装饰器
InputStream的装饰器?
答案:
java
// InputStream就是装饰器模式的典型应用
InputStream inputStream = new FileInputStream("file.txt");
inputStream = new BufferedInputStream(inputStream); // 添加缓冲功能
inputStream = new DataInputStream(inputStream); // 添加读取基本类型功能
// 装饰器链
InputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("file.txt")
)
);结构:
InputStream (抽象组件)
├── FileInputStream (具体组件)
├── ByteArrayInputStream (具体组件)
└── FilterInputStream (装饰器)
├── BufferedInputStream (具体装饰器)
├── DataInputStream (具体装饰器)
└── PushbackInputStream (具体装饰器)Spring中的装饰器
BeanWrapper?
答案:
java
// BeanWrapper是对Bean的装饰
BeanWrapper bw = new BeanWrapperImpl(new User());
bw.setPropertyValue("name", "张三");
bw.setPropertyValue("age", 20);练习题
- 装饰器模式和代理模式的区别?
- 装饰器模式和适配器模式的区别?
- 如何避免装饰器层次过深?
- Java IO为什么使用装饰器模式?
- 装饰器模式如何体现开闭原则?