一、前言

本篇主题为结构型模式中的第三个模式–装饰模式。上篇 Java 设计模式主题为《Java 设计模式之桥接模式(七)》

二、简单介绍

# 2.1 定义

装饰(Decorator)模式又叫做包装模式,其功能是动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活,是继承关系的一个替换方案。

# 2.2 参与角色

  1. Component:定义一个对象接口,可以给这些对象动态地添加职责。

  2. ConcreteComponent:定义一个对象,可以给这个对象添加一些职责。

  3. Decorator:维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致的接口。

  4. ConcreteDecorator:向组件添加职责。

# 2.3 应用场景

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  2. 当不能采用生成子类的方法进行扩充时。

三、实现方式

我们以人的打扮为例。人打扮需要穿衣,穿裤,穿鞋子。代码表示如下:

Person 类:

  1. public class Person {
  2. private String name;
  3. public Person(String name) {
  4. this.name = name;
  5. }
  6. public void putOnClothes() {
  7. System.out.println(this.name + "穿衣服");
  8. }
  9. public void putOnTrousers() {
  10. System.out.println(this.name + "穿裤子");
  11. }
  12. public void putOnShoes() {
  13. System.out.println(this.name + "穿鞋子");
  14. }
  15. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. Person person = new Person("小白");
  4. person.putOnClothes();
  5. person.putOnTrousers();
  6. person.putOnShoes();
  7. }
  8. }

打印:

  1. 小白穿衣服
  2. 小白穿裤子
  3. 小白穿鞋子

上述代码很简单,但是扩展性不好。当我们需要添加打领带、戴手表的行为时,需要修改 Person 类,违背了开放封闭原则。

因此,我们需要将人和打扮的行为抽离出来:

Person 类:

  1. public class Person {
  2. private String name;
  3. public Person(String name) {
  4. this.name = name;
  5. }
  6. public String getName() {
  7. return name;
  8. }
  9. }

打扮类:

  1. public abstract class DressUp {
  2. public abstract void dressup(Person person);
  3. }
  4. class ClothesDressUp extends DressUp {
  5. @Override
  6. public void dressup(Person person) {
  7. System.out.println(person.getName() + "穿衣服");
  8. }
  9. }
  10. class TrousersDressUp extends DressUp {
  11. @Override
  12. public void dressup(Person person) {
  13. System.out.println(person.getName() + "穿裤子");
  14. }
  15. }
  16. class ShoesDressUp extends DressUp {
  17. @Override
  18. public void dressup(Person person) {
  19. System.out.println(person.getName() + "穿鞋子");
  20. }
  21. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. Person person = new Person("小白");
  4. DressUp du1 = new ClothesDressUp();
  5. du1.dressup(person);
  6. DressUp du2 = new TrousersDressUp();
  7. du2.dressup(person);
  8. DressUp du3 = new ShoesDressUp();
  9. du3.dressup(person);
  10. }
  11. }

执行结果与上文的一致。现在,当我们添加新的打扮行为时,只需新增 DressUp 的子类即可。

但是,上边的代码没有封装性,每打扮一次都要调用 dressup 方法一次,就感觉人是光着身在公共场合进行打扮穿衣、穿鞋。因此,我们需要一种模式将这些打扮的细节封装起来,就像建造者模式一样。

不过,此次的需求不能使用建造者模式。因为建造者模式封装过程/细节是一个固定的顺序/模式,而当前需求是人的打扮,打扮的行为是多种多样的,如:穿衣穿裤、穿衣打领带、穿鞋戴手表等。

这样就引出了本章的主题--装饰模式:

Person 接口与实现类(Component 和 ConcreteComponent):

  1. public interface Person {
  2. public void decorate();
  3. }
  4. class Man implements Person {
  5. @Override
  6. public void decorate() {
  7. System.out.println("男人打扮");
  8. }
  9. }

装饰类(Decorator 和 ConcreteDecorator):

  1. public class Decorator implements Person {
  2. private Person person;
  3. public Decorator(Person person) {
  4. this.person = person;
  5. }
  6. @Override
  7. public void decorate() {
  8. this.person.decorate();
  9. }
  10. }
  11. class ClothesDecorator extends Decorator {
  12. public ClothesDecorator(Person person) {
  13. super(person);
  14. }
  15. public void decorate() {
  16. super.decorate();
  17. System.out.println("穿衣服");
  18. }
  19. }
  20. class TrousersDecorator extends Decorator {
  21. public TrousersDecorator(Person person) {
  22. super(person);
  23. }
  24. public void decorate() {
  25. super.decorate();
  26. System.out.println("穿裤子");
  27. }
  28. }
  29. class ShoesDecorator extends Decorator {
  30. public ShoesDecorator(Person person) {
  31. super(person);
  32. }
  33. public void decorate() {
  34. super.decorate();
  35. System.out.println("穿鞋子");
  36. }
  37. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. Person person = new Man();
  4. Person decorator = new Decorator(person);
  5. System.out.println("======第一种打扮=======");
  6. ClothesDecorator cd = new ClothesDecorator(decorator);
  7. TrousersDecorator td = new TrousersDecorator(cd);
  8. td.decorate();
  9. System.out.println("======第二种打扮=======");
  10. ShoesDecorator sd = new ShoesDecorator(person);
  11. sd.decorate();
  12. }
  13. }

打印:

  1. ======第一种打扮=======
  2. 男人打扮
  3. 穿衣服
  4. 穿裤子
  5. ======第二种打扮=======
  6. 男人打扮
  7. 穿鞋子

总结:装饰模式有效地把类的核心职责和装饰功能区分开来,而且去除了相关类的重复的装饰逻辑。

UML 类图表示如下: