前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的设计模式(三):装饰器模式

Java中的设计模式(三):装饰器模式

作者头像
闲宇非鱼
发布2022-02-08 11:36:07
5200
发布2022-02-08 11:36:07
举报
文章被收录于专栏:Brucebat的伪技术鱼塘

一、从逛4S店说起

  上周心血来潮去了一趟小鹏的体验店,一进门,销售的小哥就开始给我介绍各种不同配置的车,什么智享版、智尊版,听得我是头晕脑胀,赶紧告辞。

  走出店门,凉风拂面,顿觉大脑清醒了不少,不禁又开始琢磨起刚刚销售小哥跟我说的车型配置信息。琢磨了一会,突然灵光一闪,这玩意儿竟然可以用 装饰器模式 实现。

二、基本概念

1. 什么是装饰器模式

  装饰器模式是一种 对象结构型模式 ,它通过一种无须定义子类的方式来给对象动态增加职责/功能,使用对象之间的关联关系取代类之间的继承关系。其结构如下图所示:

  在上面的类图中可以看到以下四个角色:

  • Component(抽象构件):需要被装饰类的基类,同时也是装饰者的基类,在这个基类中声明了需要实现的业务方法;
  • ConcreteCompnent(具体构件):作为需要被装饰的类,在具体构件中只需要实现最基础的业务逻辑即可;
  • Decorator(抽象装饰器):抽象装饰器维护了一个指向抽象构件对象的引用(子类通过构造器等方法明确使用何种具体构件),即通过组合方式将装饰者和被装饰者建立起一个相比继承关系更为宽松的联系。同时作为抽象构件的子类,抽象装饰器还给具体构件增加了额外的职责,其额外的职责在抽象装饰器的子类中得到实现;
  • ConcreteDecorator(具体装饰器):作为抽象装饰器的子类,具体装饰器实现了需要给具体构件添加的额外职责;

2. 一个小例子

  为了更好地区分继承关系和装饰者模式的不同,下面我们分别用 继承的方式装饰者模式 来实现一下小鹏P7和P5当中的不同配置。

2.1 基于继承关系的小鹏汽车系列

  首先我们通过继承的方式来实现以下小鹏P7和P5的原始车型、附加了自动驾驶、附加了鹏翼门、附加了定制音响系统不同配置的车型。

  这里就不展示对应的代码,直接给出一张类图来阐述对应的实现。从上面的类图中我们可以看到,在不考虑配置交叉组合的情况下已经实现了8个子类,如果出现功能需要交叉组合的情况,就会出现子类保障的情况。那么在装饰器模式中,这一弊病是否会得到缓解?

2.2 基于装饰器模式的小鹏汽车系列

  有了上面的概念,我们可以尝试使用装饰者模式来实现一下小鹏汽车各种类型、各种配置的汽车。

  上面类图中具体各个类的实现如下:

PengCar

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 小鹏汽车
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:50 下午
 */
public abstract class PengCar {

    /**
     * 运行
     */
    abstract void run();
}

PengFiveCar

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 小鹏P5
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:51 下午
 */
public class PengFiveCar extends PengCar{


    @Override
    void run() {
        System.out.println("小鹏P5");
    }
}

PengSevenCar

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 小鹏P7
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:50 下午
 */
public class PengSevenCar extends PengCar{

    @Override
    void run() {
        System.out.println("小鹏P7");
    }
}

PengDecorator

代码语言:javascript
复制
package DecoratorPattern;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:52 下午
 */
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class PengDecorator extends PengCar{

    private PengCar pengCar;

    public PengDecorator(PengCar pengCar) {
        this.pengCar = pengCar;
    }

    @Override
    void run() {
       // 调用汽车本身的能力
        pengCar.run();
    }
}

AutoDriveDecorator

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 自动驾驶装饰器
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:54 下午
 */
public class AutoDriveDecorator extends PengDecorator {

    public AutoDriveDecorator(PengCar pengCar) {
        super(pengCar);
    }

    @Override
    void run() {
        super.run();
        System.out.println("增加自动驾驶!");
    }
}

PengDoorDecorator

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 鹏翼门装饰器
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:56 下午
 */
public class PengDoorDecorator extends PengDecorator{

    public PengDoorDecorator(PengCar pengCar) {
        super(pengCar);
    }

    @Override
    void run() {
        super.run();
        System.out.println("增加鹏翼门!");
    }
}

PengSpeakerDecorator

代码语言:javascript
复制
package DecoratorPattern;

/**
 * 定制音响装饰器
 *
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:57 下午
 */
public class PengSpeakerDecorator extends PengDecorator {

    public PengSpeakerDecorator(PengCar pengCar) {
        super(pengCar);
    }

    @Override
    void run() {
        super.run();
        System.out.println("增加定制音响!");
    }
}

App

代码语言:javascript
复制
package DecoratorPattern;

/**
 * @author brucebat
 * @version 1.0
 * @since Created at 2021/5/23 10:59 下午
 */
public class App {

    public static void main(String[] args) {
        // 以小鹏P5为例
        PengFiveCar pengFiveCar = new PengFiveCar();
        AutoDriveDecorator autoDriveDecorator = new AutoDriveDecorator(pengFiveCar);
        PengDoorDecorator pengDoorDecorator = new PengDoorDecorator(autoDriveDecorator);
        PengSpeakerDecorator pengSpeakerDecorator = new PengSpeakerDecorator(pengDoorDecorator);
        pengSpeakerDecorator.run();
    }
}

最终结果

代码语言:javascript
复制
小鹏P5
增加自动驾驶!
增加鹏翼门!
增加定制音响!

  从最终的示例当中可以看到,小鹏P5本身的职责和装饰器的职责相对独立,如果想要进行不同职责的排列组合,只需要使用对应的装饰器对被装饰者进行职责添加即可,无须进进行额外的子类实现。

3. 浅析优劣

3.1 装饰器模式的有点

  作为设计模式中的一种,装饰器模式可谓是将开闭原则诠释到了极致,极其灵活的实现了对象功能的扩展,而不会造成继承带来的子类个数爆炸的情况。而且在上面的例子中可以看到,在进行功能扩展的过程中可以对一个对象进行多次装饰,更加灵活地实现了多种功能的排列组合,从而创造出具有更多能力的对象。

  当然最重要的一点,也就是上面能力实现的基础,就是构件和装饰器之间通过组合方式而不是继承关系关联在一起,两者可以相对独立的进行变化运行。

3.2 装饰模式的缺点

  虽然在使用上装饰器提供了一种比继承者模式更为灵活的对象功能的扩展能力,但是这也带来了另一个问题,就是在多次装饰之后,进行调试或者问题排查时需要逐级倒推进行排查,整体的排查流程会变得较为繁琐。

三、Java IO中的装饰器模式

  如此优秀的设计模式,JDK中也有对应使用,比如其中的IO类。

  这里只挑选了 FileInputStreamBufferedInputStream 这两个类来简单欣赏下jdk中是如何使用装饰器模式的,从上面的类图可以看到, FileInputStream 就是我们上面说的具体构件,而 BufferedInputStream 则是具体装饰器。在IO中具体构件一般用于指出数据来源格式,比如上面的 FileInputStream 说明数据是从 File 文件中获取,而具体装饰器则在原本IO操作的基础上加入了额外的功能,比如在 BufferedInpuStream 通过使用缓冲流的方式对输入数据进行处理,增加了缓冲的功能。

  除了上面提到的两个类,Java IO类库中还有其他相应的类,有兴趣的同学可以阅读源码深入了解一下。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Brucebat的伪技术鱼塘 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、从逛4S店说起
  • 二、基本概念
    • 1. 什么是装饰器模式
      • 2. 一个小例子
        • 2.1 基于继承关系的小鹏汽车系列
        • 2.2 基于装饰器模式的小鹏汽车系列
      • 3. 浅析优劣
        • 3.1 装饰器模式的有点
        • 3.2 装饰模式的缺点
    • 三、Java IO中的装饰器模式
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档