设计模式 - 装饰模式

347 阅读3分钟

本文已参加【新人创作礼】活动,一起开启掘金创作之路。

概念

装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。

角色

(1)Component(抽象构件):它是具体构件和抽象装饰类的共同父类

(2)ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。

(3)Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

(4)ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

image.png

实例代码

class Decorator extends Component {
    // 抽象构件对象的引用
    private Component component;

    // 注入抽象构件子类的对象
    public Decorator(Component component) {
        this.component = component;
    }

    public void operation() {
        component.operation();
    }

}

class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        addedBehavior();
    }
    // 新增业务逻辑
    public void addedBehavior() {
        
    }
}

透明模式和半透明模式

一般代码中都是使用抽象构件来定义装饰对象:

Component cop = new ConcreteDecorator(xx);

如果客户端希望单独调用具体装饰类新增的方法,而不想通过抽象构件中声明的方法来调用新增方法时,上面方式就不适用了,需要使用具体装饰类来定义对象:

ConcreteDecorator cop = new ConcreteDecorator(xx);
cop.addedBehavior();

使用抽象构件(Component)来定义装饰对象即为透明模式;使用具体装饰类来定义对象为半透明模式。

疑问记录

  • 为什么半透明装饰模式不能实现对同一个对象的多次装饰?

因为在半透明装饰模式中,使用具体装饰类来声明装饰之后的对象,具体装饰类中新增的方法并未在抽象构件类中声明,这样做的优点在于装饰后客户端可以单独调用在具体装饰类中新增的业务方法;缺点是无法调用到之前装饰时新增的方法,只能调用到最后一次装饰时具体装饰类中新增加的方法,故对同一个对象实施多次装饰没有任何意义。(牛客网“卖火柴的R”回答

  • 试比较装饰模式和桥接模式的相同之处和不同之处

从桥接模式到装饰者(套娃)模式的思考

总结

优点

  • 扩展对象功能,装饰模式比继承更灵活,能有效降低类的数量
  • 透明模式可以对一个对象进行多次装饰
  • 具体构件类和具体装饰类可以独立变化,原有代码不用修改,符合开闭原则

缺点

  • 装饰模式比继承灵活,但是容易出错,排查困难

参考资料

[1] 刘伟. 设计模式的艺术[M]. 清华大学出版社, 2020.

[2] 从桥接模式到装饰者(套娃)模式的思考