当前位置: 首页 > news >正文

【设计模式】快速理解装饰者模式,及其在JDK源码中的应用

(一)什么是装饰者模式

装饰者模式(Decorator)的定义是:在不必改变原类文件情况下,允许向一个现有的对象添加新的功能。比如一个毛坯房,可以使用灰色去装饰厨房,可以使用白色装饰客厅,但是不管使用多少颜料去装饰这个房间,房子的本质是不会发现变化的。

使用装饰者模式除了可以在不改变原类的前提下装饰对象之外,还可以随意组合各个装饰类,比如有三个装饰类,分别是给原类上红、黄、绿三种颜色。在使用时,可以将任意两种或任意三种装饰类搭配。接下来将会用代码演示上面的这个例子。

(二)装饰者模式中的角色

装饰者模式中具备以下四种角色:

Component:一个抽象的组件类,在装饰者模式中扮演着最重要的角色。在给房子上色这个例子中,概念上的房子就是一个Component。如果还不理解,继续往下看。

ConcreteComponent:Component的具体实现,在本次的例子中就是商品房、公寓房等。

Decorator:一个通用的装饰接口或抽象类,内部保存了被装饰的Component对象。

ConcreteDecorator:具体的装饰类,在原类的基础上进行装饰。

(三)装饰者模式案例

原类是房子,可装饰红色、黄色、绿色。接下来的代码就是对这个例子的体现:
首先定义Component,也就是房子抽象类,有一个展示房子的方法:

public abstract class House {
    /**
     * 展示房子
     */
    public abstract void show();
}

接下来是ConcreteComponent,抽象房子的实现类,这里定义为商品房,ConcreteComponent可以有很多个:

public class CommercialHouse extends House{
    @Override
    public void show() {
        System.out.println("这是一个商品房");
    }
}

Decorator是一个通用的抽象类,将房子作为入参传入,并实现同样的show方法

public class HouseDecorator extends House{
    private House house;

    public HouseDecorator(House house){
        this.house = house;
    }

    @Override
    public void show() {
        house.show();
    }
}

ConcreteDecorator具体装饰类定义了三个,分别是红黄蓝装饰:

public class RedHouseDecorator extends HouseDecorator{

    public RedHouseDecorator(House house) {
        super(house);
    }
    @Override
    public void show() {
        super.show();
        System.out.println("装饰了红色");
    }
}

public class YellowHouseDecorator extends HouseDecorator{

    public YellowHouseDecorator(House house) {
        super(house);
    }
    @Override
    public void show() {
        super.show();
        System.out.println("装饰了黄色");
    }
}

public class GreenHouseDecorator extends HouseDecorator{

    public GreenHouseDecorator(House house) {
        super(house);
    }
    @Override
    public void show() {
        super.show();
        System.out.println("装饰了绿色");
    }
}

接下来就是使用了,前面已经提到了,装饰者模式除了可以在不改动原类的情况下增加功能,也可以随意组装各个装饰类:

public class Main {
    public static void main(String[] args) {
        House house = new CommercialHouse();
        //只装饰红色
        House redHouse = new RedHouseDecorator(house);
        redHouse.show();
        //装饰绿色和黄色
        House greenAndYellowHouse = new GreenHouseDecorator(new YellowHouseDecorator(house));
        greenAndYellowHouse.show();
        //三种颜色全部装饰
        House allHouse = new RedHouseDecorator(new GreenHouseDecorator(new YellowHouseDecorator(house)));
        allHouse.show();
    }
}

在上面的测试代码中,既可以只有一种颜色的装饰,也可以有两种颜色的装饰,或者是三种颜色的装饰,而原本的商品房这个类没有经过任何修改。如果用继承去实现,每种组装方式都需要新建一个类,装饰者模式的优势就体现出来了。

(四)装饰者模式在源码中的应用

看了很多装饰者模式在源码中的应用,感觉Java.IO中的流在装饰者模式的使用上最经典。以InputStream为例子,InputStream有很多的实现类:

FileInputStream:实现文件的读取。

DataInputStream:读取各种基本数据类型的数据。

BufferedInputStream:可缓存的文件流。

ObjectInputStream:读取对象的文件流。

其他的实现还有很多很多,这里的实现就使用了装饰者模式,保证InputStream不变的前提下,增加其他功能。想象一下,如果要同时实现文件读取和可缓存,那么就可以这样写:

new BufferedInputStream(new FileInputStream(""));

如果想实现基本数据类型读取+文件读取,就可以这样写:

new DataInputStream(new FileInputStream(""));

是不是和第三节的例子一模一样,在代码的实现上,其实也大同小异,InputStream是一个抽象类,定义了read方法,代码作了精简:

public abstract class InputStream implements Closeable{
    public abstract int read() throws IOException;
}

BufferedInputStream是InputStream子类的子类,在继承关系上,BufferedInputStream继承FilterInputStream,FilterInputStream继承InputStream,这里相当于对装饰类进行了再扩展,看一下FilterInputStream:

public class FilterInputStream extends InputStream{
    protected volatile InputStream in;
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    public int read() throws IOException {
        return in.read();
    }
}

而BufferedInputStream以及DataInputStream都是对FilterInputStream再做一些功能上的增强,很巧妙的实现了在不必改变原类文件情况下,允许向一个现有的对象添加新的功能。

(五)总结

装饰者模式很好地体现了设计模式中的开闭原则,即类应该对扩展开放,对修改关闭。值得深深品味。

相关文章:

  • 你真的了解Maven吗?
  • 【转】Xcode常用快捷键与技巧分享
  • 【设计模式】快速理解观察者模式,原来它还有这么多其他名字
  • linux实际应用小技巧
  • 时间类有多复杂,JDK竟设计了三版
  • AOP之PostSharp5-LocationInterceptionAspect
  • 如何快速学习一门新技术
  • 模拟实现部分库函数(strcpy,strcmp,strcat,strstr,memcpy,memmove,memset)
  • 组成原理(一):计算机是如何组成的
  • JDK9相比于JDK8,究竟变强了多少
  • Hive之分区(Partitions)和桶(Buckets)
  • 列式存储?OLAP?ClickHouse究竟是何方神圣
  • 分享Open-E DSS V7 应用系列十篇!
  • 基于SpringBoot和BootStrap的全栈论坛网站(附上源码)
  • 我的Java全系列技术博客
  • 自己简单写的 事件订阅机制
  • [deviceone开发]-do_Webview的基本示例
  • 《Java编程思想》读书笔记-对象导论
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • Apache Spark Streaming 使用实例
  • CAP 一致性协议及应用解析
  • gf框架之分页模块(五) - 自定义分页
  • Leetcode 27 Remove Element
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • select2 取值 遍历 设置默认值
  • Vue全家桶实现一个Web App
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 数组的操作
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 我们雇佣了一只大猴子...
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • ​iOS安全加固方法及实现
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • # Java NIO(一)FileChannel
  • %@ page import=%的用法
  • (+4)2.2UML建模图
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (7)STL算法之交换赋值
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (poj1.2.1)1970(筛选法模拟)
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (黑马C++)L06 重载与继承
  • (四)汇编语言——简单程序
  • (轉貼) UML中文FAQ (OO) (UML)
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • ./configure、make、make install 命令
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .NET CLR基本术语
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NetCore 如何动态路由
  • .net连接oracle数据库