前言
不知道是不是因为直译的缘故,很多Java技术或者说软工方面的术语都显得很书面化,初闻就给人一种很高大上的感觉,但事实上,其原理和应用场景都很易懂。
什么是装饰器模式
定义
在Java中,修饰器(Decorator)模式是一种结构型设计模式,它允许向对象添加新的功能,同时保持良好的封装性。这种模式通过创建包含被装饰对象的对象来工作,这样可以在运行时动态地给一个对象添加职责,而无需使用子类或者影响其他对象。
个人理解
书面化定义比较难以直观理解,而且一种有用的设计思想,总能提炼出很多适用场景,演化出很多适用的背景。就我的理解而言,装饰器模式的核心作用在于解决滥用继承的问题。
首先,继承是实现代码复用的一种强大方法,但除非超类是专门为扩展的目的而设计的,否则一旦父类的实现发生了改变,所有继承自它的子类都必须相应地调整,甚至父类一旦增加或修改方法,都可能使得子类产生安全问题。
其次,即使我们保证了超类是专门为扩展的目的而设计的,子类的数量控制也是一个问题,假设我们要给某个类加上三个基础功能,那么我们或许需要(2^3 - 1 = 7)个子类来覆盖所有可能的功能组合。
因而,在非必要情况下,与其扩展现有类,不如为新类提供一个引用现有类实例的私有字段。因为现有的类是新类的一个组件,新类中的实例方法仍然调用现有类实例的对应方法。
使用场景
- 动态添加功能:在不修改对象的情况下,动态地给对象添加新的功能。
- 替代继承:当继承层次结构过于复杂时,使用装饰器模式来替代继承,减少子类数量。
- 组合功能:根据需要组合不同的功能,使对象具有多种特性。
装饰器模式的实现方式
组成部分
- Component(组件):定义一个对象接口,可以给这些对象动态地添加职责。比如说,饮料描述。
- Concrete Component(具体组件):定义一个对象,可以给它添加一些职责。比如说,具体到奶茶描述。
- Decorator(装饰器):持有Component类型的引用,并且定义一个与Component接口一致的接口。比如说,饮料加小料。
- Concrete Decorator(具体装饰器):负责给Component对象添加职责。比如说,奶茶加珍珠描述。
一个简单例子
完成一个不同饮品加多种小料的例子。
假设我们有一个Beverage
接口,代表饮料的基本特性,比如获取描述和价格。
public interface Beverage {
String getDescription();
double cost();
}
然后定义几种具体的饮料实现这个接口:
public class MilkTea implements Beverage {
@Override
public String getDescription() {
return "奶茶";
}
@Override
public double cost() {
return 5;
}
}
接下来,定义一个装饰器接口,它继承自Beverage
接口:
public abstract class CondimentDecorator implements Beverage {//加料
@Override
public abstract String getDescription();
}
然后定义具体的装饰器类,比如添加pearl的装饰器:
public class Pearl extends CondimentDecorator {
private Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 珍珠";
}
@Override
public double cost() {
return 3 + beverage.cost();
}
}
最后,使用这些类:
public class Main {
public static void main(String[] args) {
Beverage beverage = new MilkTea();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
// 添加装饰器
beverage = new Pearl(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
}
}
注意事项
- 过度使用装饰器模式会导致层次过于复杂。
- 应该明确设计目的,避免随意添加装饰器。