【设计模式】为什么装饰器模式能够取代继承?

1,695 阅读4分钟

这是我参与更文挑战的第30天,活动详情查看:更文挑战


👉设计模式目录

什么是装饰器模式(Decorator )

概念

装饰器模式(Decorator Pattern)属于结构型模式,是在不改变现有的对象结构(代码)的情况下,对这个对象动态添加一些新的职责(功能),希望通过用组合对象的方式替代继承的方式来实现对类的扩展

我们看它的名字就知道,装饰器,像我们带的手表,就能够不影响我们,然后赋予我们看时间的能力。

蜡笔小新看手表

装饰器模式是在不改变核心功能的基础上去新增一些新的功能,和代理模式非常地相似,在下面学习完如何实现装饰器模式后会做一下区分的总结。

优点

  1. 装饰器模式是继承的一种替代方式,通过组合的方式完成继承的功能,但却避免了继承的侵入性。
  2. 降低类间的耦合。被装饰类和装饰类都可以独立发展,不会相互影响。
  3. 符合开放封闭原则。

有一说一套娃

缺点

过度使用装饰器模式会导致类的数量变得庞大,也会增加程序的复杂度。

设计模式虽好,不要贪杯哦。

原则

“+”代表遵守,“-”代表不遵守或者不相关

原则开放封闭单一职责迪米特里氏替换依赖倒置接口隔离合成复用
+++-+++

适用场景(动机)

  1. 想要避免使用继承来扩展功能。
  2. 实现的子类有不同的维度。

如何实现

想要实现装饰器模式,需要有以下四样东西:

  1. 抽象构件接口:定义具体构件类的接口。
  2. 具体构件类(被装饰类):实现抽象构件接口,创建要给被修饰类。
  3. 装饰抽象类:实现抽象构件,规定实现类要实现的方法,并组合具体构建类的对象。
  4. 具体装饰类:继承装饰抽象类,实现方法。

上类图

image-20210602160851311

上代码

抽象构件接口:Component

/**
 *
 * 抽象构件接口
 * 模拟人类
 * Created on 2021/6/2.
 *
 * @author xuxiaobai
 */
public interface Component {

    void say();

    void eat();
}

具体构件类:ConcreteComponent

/**
 * 具体构件类
 * 例如,翠花类
 * Created on 2021/6/2.
 *
 * @author xuxiaobai
 */
public class ConcreteComponent  implements Component{
    @Override
    public void say() {
        System.out.println("你好,我叫翠花。");
    }

    @Override
    public void eat() {
        System.out.println("真好吃!");
    }
}

装饰抽象类:Decorator

/**
 * 装饰抽象类
 * Created on 2021/6/2.
 *
 * @author xuxiaobai
 */
public abstract class Decorator implements Component{
    Component component=new ConcreteComponent();

    @Override
    public void say() {
        component.say();
    }

    @Override
    public void eat() {
        component.eat();
    }

    /**
     * 看时间
     */
    public abstract void lookTime();
}

具体装饰类:ConcreteDecorator

/**
 * 具体装饰类
 * 带上手表的翠花类
 * Created on 2021/6/2.
 *
 * @author xuxiaobai
 */
public class ConcreteDecorator extends Decorator{

    @Override
    public void lookTime() {
        System.out.println("我看到我的小米手环显示,现在是09:30");
    }
}

测试:

/**
 * Created on 2021/6/2.
 *
 * @author xuxiaobai
 */
public class DecoratorTest {
    public static void main(String[] args) {
        /**
         * 没有手表的翠花类
         */
        Component component = new ConcreteComponent();
        component.say();
        component.eat();

        /**
         * 有手表的翠花类
         */
        Decorator decorator = new ConcreteDecorator();
        decorator.say();
        decorator.eat();
        decorator.lookTime();

        /**
         * 结果:
         * 你好,我叫翠花。
         * 真好吃!
         * 你好,我叫翠花。
         * 真好吃!
         * 我看到我的小米手环显示,现在是09:30
         */
    }
}

这里的翠花想戴手表就戴(Decorator),不想戴就不戴(ConcreteComponent),非常地灵活。

总结

跟代理模式的区别:

装饰器模式跟静态代理非常地相似,代理模式也有采用静态代理的方式去实现代理的,两者之间有什么区别呢?

有的,装饰器模式的重点在于扩展新的方法,像上面的例子,翠花除了说话和吃,还扩展了看时间的方法;而代理模式则是为每个原有方法附加新的功能,例如翠花想打包KFC回家吃,可以自己去店里点,也可以在KFC小程序上点,相当于由小程序帮你转告店员你要点餐,做的事情跟自己去店里点做的事情是一样的,只是现在不需要自己去店里了。

我个人认为,虽然装饰器模式是继承的一种替代,但在想要新扩展的类数量不是很多时,可以考虑直接使用继承的方式来扩展,等扩展类的数量稍微有点多了,再对这一堆类进行一个重构,重构成装饰器模式。

我是一个没有感情的程序员

——————————————————————————————

你知道的越多,不知道的就越多。

如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦

未经允许,不得转载!