初探设计模式——装饰者模式

201 阅读5分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

1.模式动机

一般情况下要给一个类或者对象增加新的功能有两种方式:

  • 继承:继承的好处是子类在拥有自身功能的同时还可以拥有父类的功能,但是这种方法是静态的,用户不能控制增加行为的方式和时机;
  • 关联方式:将一个类的对象嵌入到另一个对象中,由另一个对象决定是否调用嵌入对象的方法来以便扩展自己的行为,我们称这种嵌入的对象为对象装饰器。

装饰模式是以对客户透明的方式动态的给一个对象增加新的功能,这种装饰的流程对于与客户端来讲并不重要,因为客户端并不知道装饰前后的区别是什么,这样的好处就是不需要增加新的子类就可以扩展对象的功能。

2.定义

动态的给一个对象增加一些额外的功能,并且就以增加功能来说,装饰模式要比生成子类的方式更灵活。

3.模式结构

四个角色:

  • 抽象组件
  • 具体组件
  • 抽象装饰器
  • 具体装饰器

4.时序图

5.代码分析

代码案例:有一个变形金刚,没变形之前是一辆车可以移动,但是经过变形后他可以说话,如果需要他也可以飞行

有以下四个角色:

  • 抽象组件:Transform
  • 具体组件:Car
  • 装饰者:Changer
  • 具体装饰者:Robot、Airplane
/**
 * 抽象组件:变形金刚
 */
public static abstract class Transform {
    public abstract void move();
}

/**
 * 具体组件:汽车人
 */
public static class Car extends Transform {

    @Override
    public void move() {
        System.out.println("可以移动");
    }
}

/**
 * 装饰者
 */
public static class Changer extends Transform {
    private Transform transform;

    public Changer(Transform transform) {
        this.transform = transform;
    }

    @Override
    public void move() {
        transform.move();
    }
}

/**
 * 具体装饰者:可以说话
 */
public static class Robot extends Changer {

    public Robot(Transform transform) {
        super(transform);
    }

    @Override
    public void move() {
        super.move();
        say();
    }

    public void say() {
        System.out.println("可以说话");
    }
}

/**
 * 具体装饰者:可以飞行
 */
public static class Airplane extends Changer {

    public Airplane(Transform transform) {
        super(transform);
    }

    @Override
    public void move() {
        super.move();
        fly();
    }

    public void fly() {
        System.out.println("可以飞");
    }
}

/**
 * 具体使用
 */
public static void main(String[] args) {
    Transform transform = new Car();
    Changer robot = new Robot(transform);
    robot.move();
    System.out.println("---------------------------");
    Changer airPlane = new Airplane(transform);
    airPlane.move();
}

运行结果:

可以移动
可以说话
---------------------------
可以移动
可以飞

6.模式分析

与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承关系是一种耦合度较大的静态关系,因此无法在程序运行时动态扩展。在开发阶段与继承关系相比并没有编码上的减少,但是在维护阶段优势就体现出来了,因为它具有良好的松耦合关系,因此是的系统更加容易维护。使用装饰模式可以在不需要创建更多的子类的情况下扩展更多的功能。

7.优点

  • 装饰模式与继承关系的目的都是扩展对象的功能,但是装饰模式可以提供比继承关系更多的灵活性;
  • 装饰模式可以以一种动态的方式扩展,在程序运行期间可以通过配置文件对其进行扩展;
  • 通过不同的具体装饰类可以实现更多的排列组合,可以使用多个装饰类来装饰同一个对象使其功能更强大;
  • 具体构建类和具体装饰类可以改变也可以新增且无需修改原有代码,符合【开闭原则】。

8.缺点

  • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
  • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

9.适用环境

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类)

10.扩展

  • 一个装饰类的接口必须与被装饰类的接口相同,这样可以保证客户端对于装饰前的对象和装饰后的对象一致对待;
  • 尽量保持具体构建类的逻辑简洁,不要把台多的逻辑和状态放在具体构建类中,如果需要则通过装饰器实现。
  • 如果只有一个具体的构建类那么可以把抽象构建类作为具体构建类。

参考链接:装饰者模式