装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变原有对象结构的情况下,动态地给对象添加新功能。装饰模式通过创建 “装饰器” 类来包裹原始对象,从而在保持接口一致性的同时,实现功能的扩展。
打个比方,装饰模式就像 “手机壳”—— 手机本身的功能不变,但加上不同的手机壳(装饰器)可以获得防摔、美观、无线充电等额外功能,且可以自由组合多个装饰器。
装饰模式的UML类图如下:

从上面的类图中我们可以看出,在装饰模式中:
- Component:是一个抽象组件,它定义了被装饰对象的公共接口。也就是说,客户端能访问的被装饰组件的通用的方法。
- Concretecomponent:是实际上的被装饰对象,它实现了Component接口,也意味着每个实际的被装饰对象必须实现抽象的公共接口中的方法,也就是通用功能必须具备。
- Decorator:装饰器的抽象类,在这里它要持有某个被装饰的对象的抽象,然后通过后面的具体装饰器继承它来实现被装饰组件功能的叠加。
- ConcreteDecorator:具体的装饰器,它通过继承Decorator来给被装饰组件增加额外的功能。
我们还是通过一个具体的例子来理解它。比如,我们喝的咖啡有各种口味的,其实都是在基础的咖啡液中增加奶、糖、巧克力等配料来形成不同的口味,当然价格也会不一样。我们可以抽象出一个咖啡产品都有“什么口味和多少钱一杯”这两个信息。
那么,首先我们来创建咖啡这个被装饰组件的抽象接口:
1 2 3 4 5
| interface Coffee { String getDescription(); double getCost(); }
|
那么,最基本的开发可能是浓缩咖啡,我们来实现它:
1 2 3 4 5 6 7 8 9 10 11 12
| class Espresso implements Coffee { @Override public String getDescription() { return "浓缩咖啡"; } @Override public double getCost() { return 25.0; } }
|
接下来,加入不同的配料就有了不同口味和价格的咖啡,因此我们可以”装饰“这个咖啡了。
先来创建一个抽象的咖啡装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } @Override public String getDescription() { return coffee.getDescription(); } @Override public double getCost() { return coffee.getCost(); } }
|
这个装饰器继承了Coffee类,也就是说,无论是怎么装饰,它本身都是一杯咖啡。
接下来我们创建牛奶咖啡的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return coffee.getDescription() + " + 牛奶"; } @Override public double getCost() { return coffee.getCost() + 5.0; } }
|
从代码中我们不难看出,牛奶咖啡首先必须是一种咖啡,所以我们在构造的时候要把coffee参数传入进来,这部分是不变的。然后,牛奶咖啡的描述和价格是其特有的,这部分是变动的,我们在装饰器里完成这部分变动的内容。
以此类推,我们继续实现加糖和加巧克力 的装饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return coffee.getDescription() + " + 糖"; } @Override public double getCost() { return coffee.getCost() + 2.0; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class ChocolateDecorator extends CoffeeDecorator { public ChocolateDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return coffee.getDescription() + " + 巧克力"; } @Override public double getCost() { return coffee.getCost() + 8.0; } }
|
现在关于这个咖啡的装饰器我们已经做好了,客户来点咖啡的时候,就可以根据要求加不同的配料来。在实际的软件开发中,也就是给一个组件增加不同的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class CoffeeShop { public static void main(String[] args) { Coffee coffee = new Espresso(); System.out.println(coffee.getDescription() + ",价格:" + coffee.getCost() + "元"); coffee = new MilkDecorator(coffee); System.out.println(coffee.getDescription() + ",价格:" + coffee.getCost() + "元"); coffee = new ChocolateDecorator(coffee); System.out.println(coffee.getDescription() + ",价格:" + coffee.getCost() + "元"); coffee = new SugarDecorator(coffee); System.out.println(coffee.getDescription() + ",价格:" + coffee.getCost() + "元"); } }
|
从例子中我们可以看出,装饰器的优势在于:
- 动态扩展:可以在运行时为对象添加功能,如示例中动态组合各种配料
- 灵活组合:装饰器可以任意组合,实现多种功能组合(如牛奶 + 糖、巧克力 + 牛奶等)
- 避免类爆炸:相比为每种组合创建单独的类(如加牛奶的咖啡、加牛奶和糖的咖啡),装饰模式大大减少了类的数量。