肝一肝设计模式【六】-- 装饰器模式

news2024/9/22 9:38:20

系列文章目录

肝一肝设计模式【一】-- 单例模式 传送门
肝一肝设计模式【二】-- 工厂模式 传送门
肝一肝设计模式【三】-- 原型模式 传送门
肝一肝设计模式【四】-- 建造者模式 传送门
肝一肝设计模式【五】-- 适配器模式 传送门


文章目录

  • 系列文章目录
  • 前言
  • 一、什么是装饰器模式
  • 二、装饰器模式中的角色
  • 三、举个栗子
  • 四、在JDK中的应用
  • 写在最后


前言

我们知道设计模式可以分为三大类:创建型模式、结构型模式、行为型模式。上一节我们分析了结构型模式中的适配器模式,这一节我们继续分析另外一种结构型模式——装饰器模式


一、什么是装饰器模式

装饰器模式(Decorator Pattern),它允许在不改变已有对象的基础上动态地扩展其功能。通过将对象包装在装饰器对象中,可以在运行时为对象添加额外的行为,就增加功能来说,装饰器模式比生成子类更灵活。

装饰器模式的关键是使用组合而非继承来实现功能的扩展。通过将对象包装在装饰器对象中,可以按需添加或修改对象的行为,而无需改变原始对象或其他代码。

二、装饰器模式中的角色

在装饰器模式中,有一个核心的接口或抽象类,代表要被装饰的对象。

然后,创建一个装饰器类,它实现了相同的接口或继承了相同的抽象类,并在构造函数中接受被装饰的对象作为参数。

装饰器类中包含一个与被装饰对象相同的成员变量,用于保存被装饰的对象。

在装饰器类中,可以调用被装饰对象的方法,并可以在方法调用前后添加额外的逻辑。

以下是装饰器模式的几个关键角色:

  • Component(抽象组件):定义了被装饰对象的接口或抽象类。
  • ConcreteComponent(具体组件):实现了组件接口或抽象类,是被装饰的对象。
  • Decorator(装饰器):实现了组件接口或抽象类,并在构造函数中接受被装饰对象。装饰器可以调用被装饰对象的方法,并可以添加额外的功能。
  • ConcreteDecorator(具体装饰器):扩展了装饰器类,实现了额外的功能。

三、举个栗子

说了这么多的概念,感觉懂了又好像没懂,那就必须举个栗子了,我在第一次了解装饰器模式的时候,第一时间就想到了玩一款RPG游戏,你可以为你的角色穿上装备,也可以不穿装备硬刚ヽ( ̄▽ ̄)ノ
在这里插入图片描述
首先创建一个基础的角色类(Character)作为抽象组件,并定义一个角色的基本操作:

public interface Character {
    void attack();
}

然后我们再建一个具体的角色类,比如新建一个战士(Warrior),实现角色接口:

public class Warrior implements Character {
    @Override
    public void attack() {
        System.out.println("赤手空拳的攻击!");
    }
}

现在我们准备给我们的战士加点装备,比如使用武器或者戴上防具,这时就可以使用装饰器模式了。

首先先定义一个装饰器抽象类(Decorator),也实现了角色接口:

public abstract class Decorator implements Character {

    protected Character character;

    public Decorator(Character character) {
        this.character = character;
    }

    @Override
    public void attack() {
        character.attack();
    }
}

然后,创建具体的装饰器类来实现附加功能。
比如,创建一个武器装饰器类(WeaponDecorator):

public class WeaponDecorator extends Decorator {

    public WeaponDecorator(Character character) {
        super(character);
    }

    @Override
    public void attack() {
        super.attack();
        addWeapon();
    }

    private void addWeapon() {
        System.out.println("装上利维坦之斧削你!");
    }
}

再创建一个防具装饰器类(ArmorDecorator):守护者之盾

public class ArmorDecorator extends Decorator {

    public ArmorDecorator(Character character) {
        super(character);
    }

    @Override
    public void attack() {
        super.attack();
        addArmor();
    }

    private void addArmor() {
        System.out.println("戴上守护者之盾我防!");
    }
}

测试一下

public class DecoratorClient {
    public static void main(String[] args) {
        Character warrior = new Warrior();
		warrior.attack();  // 输出:赤手空拳的攻击!
		
		Character warriorWithWeapon = new WeaponDecorator(warrior);
		warriorWithWeapon.attack();  // 输出:赤手空拳的攻击!装上利维坦之斧削你!
		
		Character warriorWithWeaponAndArmor = new ArmorDecorator(warriorWithWeapon);
		warriorWithWeaponAndArmor.attack();  // 输出:赤手空拳的攻击!装上利维坦之斧削你!戴上守护者之盾我防!
    }
}

通过装饰器模式,我们可以动态地为玩具对象添加不同的装饰器,以实现不同的附加功能,而无需修改原有的玩具类或客户端代码。这种方式可以灵活地组合和扩展功能,同时保持代码的清晰

四、在JDK中的应用

JDK中使用装饰器模式的地方,一个典型的例子就是Java I/O库中使用 BufferedInputStreamBufferedOutputStream 类对输入流和输出流进行包装和装饰。

简单示例:

public class DecoratorClient {
    public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("input.txt");
            FileOutputStream fileOutputStream = new FileOutputStream("output.txt");

            // 使用装饰器对输入流进行包装和装饰
            BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

            // 使用装饰器对输出流进行包装和装饰
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);

            // 读取和写入数据
            int data;
            while ((data = bufferedInputStream.read()) != -1) {
                bufferedOutputStream.write(data);
            }

            // 关闭流
            bufferedInputStream.close();
            bufferedOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在示例当中,我们使用了 FileInputStreamFileOutputStream 来创建原始的输入流和输出流。然后,我们分别使用 BufferedInputStreamBufferedOutputStream 对这些原始流进行装饰。

BufferedInputStream为例,看一下源码摘要(省略了一些方法):

public class BufferedInputStream extends FilterInputStream {
    // ...

    public BufferedInputStream(InputStream in) {
        super(in);
        // ...
    }

    // ...
}

在上述代码中,BufferedInputStream 继承了 FilterInputStream 类,FilterInputStream 本身是一个抽象类,也是装饰器类。FilterInputStream 继承了 InputStream 抽象类,从而与原始的输入流进行连接。

构造函数 BufferedInputStream(InputStream in) 接受一个原始的输入流对象作为参数,并通过调用 super(in) 将其传递给父类 FilterInputStream 的构造函数。

通过这样的继承关系和构造函数的调用,BufferedInputStream 实际上是一个装饰器类,它可以包装和装饰任何实现了 InputStream 接口的输入流对象。

BufferedInputStream 通过添加缓冲功能来装饰输入流。它维护了一个内部缓冲区,通过调用原始输入流的 read() 方法将数据从原始流读入到缓冲区中,并通过提供额外的方法(如 read(byte[] b, int off, int len))来从缓冲区中读取数据,以提高读取的性能。


写在最后

装饰器模式的优点:

  • 可以在不修改已有代码的情况下扩展对象的功能。
  • 允许多个装饰器按照一定顺序进行组合,以实现复杂的功能组合。
  • 装饰器与被装饰对象之间是松耦合的,可以独立地进行扩展和修改。

装饰器模式的缺点:

  • 会导致类的增多,增加了代码复杂性,过度使用装饰器模式也可能使代码难以理解和维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/494404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PLSQL Developer远程连接Oracle报错提示:“ORA-12541:TNS:无监听程序”的解决方案及思路

环境: 1、Windows Server 2、远程Oracle数据库:Oracle11g R2 3、PL/SQL程序:PL/SQL Developer Version 11.0.5.1790 (64 bit) 一、思路拆解: 此现象一般定位到远端的监听服务来找问题,在远端查看监听服务状态&#x…

DolphinScheduler 3.1.4详细教程

文章目录 第一章 DolphinScheduler介绍1.1 关于DolphinScheduler1.2 特性1.3 名词解释1.3.1 名词解释1.3.2 模块介绍 第二章 DolphinScheduler系统架构2.1 系统架构图2.2 架构说明该服务包含: 2.3 启动流程活动图2.4 架构设计思想2.4.1 去中心化vs中心化2.4.1.1 中心…

制作帮助中心过程中常见的误区与解决方法?

制作帮助中心是为了帮助用户了解产品和解决问题的重要手段。然而,在制作的过程中,我们可能会遇到一些误区,这些误区可能会导致我们的帮助中心无法达到预期的效果。因此,在本文中,我们将探讨制作帮助中心过程中常见的误…

今年产品经理这么卷,大家怎么面试的呢?

随着互联网行业的快速发展,产品经理这个职位变得越来越重要。产品经理需要具备多方面的能力,如市场调研、用户需求分析、产品设计、项目管理等等。因此,对于企业来说,招聘一名合适的产品经理是非常关键的。 而对于求职者来说&…

中国奇人周兴和——记四川星河建材有限公司董事长周兴和(中)

经商送礼 关押收审 从10多岁就开始闯荡社会的周兴和,可以说是社会大熔炉锻炼出来的奇人。他仅有小学文化程度,但是他的社会经验和社会知识却相当丰富,可以称得上是一个“社会学专家”。他口才超常,其处事能力、应变能力和综合能力…

ASEMI代理ADUM1250ARZ-RL7原装ADI车规级ADUM1250ARZ-RL7

编辑:ll ASEMI代理ADUM1250ARZ-RL7原装ADI车规级ADUM1250ARZ-RL7 型号:ADUM1250ARZ-RL7 品牌:ADI/亚德诺 封装:SOIC-8-150mil 批号:2023 引脚数量:8 工作温度:-40C~105C 安装类型&#…

4.7亿加元!爱立信和加拿大政府巨额投资量子计算

​ (图片来源:网络) 爱立信与加拿大政府的研发合作为期五年,项目价值超过4.7亿加元(约合23.9亿元人民币),主要投入在爱立信位于安大略省渥太华和魁北克省蒙特利尔的工厂。预计将创造数百个工作…

三十岁成功入职京东啦!

我是小九小九不爱喝酒: 自己工作5年后,我成功拿到了京东的offer。下面说下我是如何从传统行业到京东的经历,希望能对你有所帮助。 本科我学的是机械电子工程专业,2013年本科毕业后,同学们大多到各研究所从事智能机器人…

真题详解(归纳法)-软件设计(六十七)

真题详解(关系模型)-软件设计(六十六)https://blog.csdn.net/ke1ying/article/details/130495791 1、2018上半年 将小阶向大阶对奇,尾数右移动 解析: 0.23 * 10的2次方 0.22 *10的3次方 第一步:0.023*10的3次方,…

2022年收入增长23%,23年Q1大涨85%,上市后英方软件交出了一份亮眼的答卷

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 2023年4月26日,英方软件(688435)公布了上市后首个业绩报告,包括2022年度财报和2023年第一季度财报。披露财报显示,英方软件在2022年实现营业收入1.97亿元&#xff…

ZC706P试验PL_DDR3内存条的步骤方法

ZC706P 板卡完全兼容XILINX官方的ZC706,当然也支持PL外挂的1G的DDR3内存条,这个片BLOG我提供从官方下载的一个文档和一个项目,演示一下验证DDR3的步骤。 步骤1:准备好板子,安装好软件。 链接:https://pan.baidu.com/s…

《创造》人生旅程

上个月读完的这本《创造》,发现作者是iPod的创造者,让我回想起我刚工作时买的iPod。iPod确是惊艳之作,那时我一个月挣1000元,竟毫不犹豫花了2000就买了,并一直伴随我直到进入智能手机时代。《创造》会写什么&#xff1…

日志项目之——将kafka数据存入hbase中

目录 1.添加依赖 2.在hbase shell界面中分别输入下面的语句&#xff0c;创建namespace和表 3.UserFriendToHB 4.UsersToHB 5.TrainToHB 6.EventsToHB 7.EventAttendeToHb 1.添加依赖 <dependencies><dependency><groupId>junit</groupId><a…

软考信管高级——成本管理

成本管理内容 成本基准 成本基准是经过批准的&#xff0c;不包括管理储备&#xff0c;只有正式变更流程才能变更成本基准成本基准管理储备项目预算&#xff0c;有必要动用管理储备变更时&#xff0c;获变更批准后&#xff0c;把适量管理储备移入成本基准中 应急储备和管理储备…

Map接口以及Collections工具类

文章目录 1.Map接口概述1.1 Map的实现类的结构1.2 Map中存储的key-value结构的理解1.3 HashMap的底层实现原理(以JDK7为例)1.4 Map接口的常用方法1.5 TreeMap1.6 Map实现类之五: Properties 1.Collections工具类1.1方法1.1.1 排序操作(均为static方法)1.1.2 查找、替换 1.Map接…

PMP项目管理-[第八章]质量管理

质量管理知识体系&#xff1a; 规划质量管理&#xff1a; 管理质量&#xff1a; 控制质量 &#xff1a; 8.1 质量和等级的区别 质量定义&#xff1a;作为实现的性能或成果&#xff0c;是一系列内在特性满足要求的程度 等级定义&#xff1a;作为设计意图&#xff0c;是对用途相同…

Android 历代版本主要变化

这里只站在开发者的角度&#xff0c;谈论一下 Android 5.0 之后的版本的主要变化 Android 5.0 L 开始支持64位的处理器开始全面由 Dalvik 虚拟机转成ART虚拟机 Android 6.0 M 增加全新的动态权限机制&#xff08;运行时权限&#xff09;取消支持 Apache HTTP 客户端 Andro…

队列,双端队列,栈结构

java.util.Queue接口.队列 Queue继承自Collection. 队列可以保存一组元素,但是存取元素必须遵循先进先出原则:FIFO(First Input First Output) 常用实现类:LinkedList 双端队列 java.util.Deque Deque继承自Queue 双端队列是队列两端都可以做出入对操作的队列. 常用实现类…

适用于Win和Mac的专业电脑数据恢复软件EasyRecovery易恢复14

EasyRecovery易恢复一键轻松找回丢失数据 支持电脑、相机、移动硬盘、U盘、SD卡、内存卡、光盘、本地电子邮件和 RAID 磁盘阵列等各类存储设备的数据恢复。EasyRecovery还可以支持恢复从硬盘、光盘、U盘、数码相机、手机等各种设备中恢复被删除或丢失的文件&#xff0c;只是使用…

分享kubernetes部署:cachecloud部署说明

cachecloud部署 cachecloud是搜狐视频(sohutv)Redis私有云平台 已省略~ 挑选一台服务器部署cachecloud-web 将cachecloud-web打成war包&#xff0c;可以在服务器上打包&#xff0c;也可以在本地打包&#xff0c;这里举一个在服务器上打包的例子。 将源代码下载到/opt下 已省略~…