第八章结构型模式—装饰者模式

文章目录

  • 装饰者模式
    • 解决的问题
    • 概念
    • 结构
  • 案例
    • 使用装配者进行改进
  • 使用场景
    • JDK源码分析
  • 静态代理和装饰者的区别

结构型模式描述如何将类或对象按某种布局组成更大的结构,有以下两种:

  • 类结构型模式:采用继承机制来组织接口和类。
  • 对象结构型模式:釆用组合或聚合来组合对象。

由于组合关系或聚合关系比继承关系耦合度低,满足 “合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。

结构型模式分为以下 7 种:

  • 代理模式
  • 适配器模式
  • 装饰者模式
  • 桥接模式
  • 外观模式
  • 组合模式
  • 享元模式

装饰者模式

解决的问题

img

  • 假如我们有一个初代的机器人,他具有对话,唱歌,放音乐的功能,如果我们想要一个新的功能,拖地和跳舞
  • 第一种方式就是我们用第二代机器人,第二代机器人利用继承了第一代机器的基础上进行扩展,如果我们想要新的功能,就需要第三代机器人(因为要满足开放闭合的原则)
    • 由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
    • 我们要引入一个有功能为飞行的机器人就需要重新定义一个类,如果有一天又想要一个游泳的功能,又得重新定义一个类,哪天不想要飞行功能了,又得重新定义一个类来只实现游泳
  • 第二种模式就用我们的装饰器模式,我们类似在初代机器人上套一个盒子,然后在盒子上进行功能的扩展 这样就可以扩展一个类的功能。 动态增加功能,动态撤销。
    • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

概念

**装饰者模式:**在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

结构

装饰者(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

案例

在这里插入图片描述

先来看一个快餐店的例子:快餐店有炒面、炒饭等快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。下面是使用继承方式的类图:

使用继承的方式存在的问题:

  • 扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给 FriedRice 和 FriedNoodles 分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。
  • 产生过多的子类

使用装配者进行改进

采用使用装饰者模式对快餐店案例进行改进,类图如下:

image-20230504141909725

抽象构件角色—快餐类

@Data
public abstract class FastFood {
    private float price;//价格
    private String desc;//描述

    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }
    public abstract float cost();
}

具体构建角色—炒饭 炒面

public class FireRice extends FastFood{

    public FireRice() {
        super(10,"炒饭");
    }

    @Override
    public float cost() {
        return getPrice();
    }
}
public class FireNoodles extends FastFood{
    public FireNoodles() {
        super(12, "炒面");
    }

    @Override
    public float cost() {
        return getPrice();
    }
}

抽象装饰者角色:配料类

public abstract class Garnish extends FastFood{
    //声明快餐类的变量
    private FastFood fastFood;

    public Garnish(FastFood fastFood,float price,String desc){
        super(price,desc);
        this.fastFood=fastFood;
    }

}
  • 为什么我们的配料类要求继承我们的FastFood? 有什么意义呢?等后面的测试时的分析

具体的装饰者角色 鸡蛋和培根

public class Egg extends Garnish {
    public Egg(FastFood fastFood) {
        super(fastFood, 1, "鸡蛋");
    }

    // 计算价格
    public float cost() {
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
public class Bacon extends Garnish {
    public Bacon(FastFood fastFood) {
        super(fastFood, 2, "培根");
    }

    // 计算价格
    public float cost() {
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

测试

public class Client {
    public static void main(String[] args) {
        //  点一份炒饭
        FastFood fastFood=new FireNoodles();
        System.out.println(fastFood.getDesc() + "  " + fastFood.cost() + "元");
        //加一个鸡蛋
        fastFood=new Egg(fastFood);
        System.out.println(fastFood.getDesc() + "  " + fastFood.cost() + "元");
        //加一个培根
        fastFood=new Bacon(fastFood);
        System.out.println(fastFood.getDesc() + "  " + fastFood.cost() + "元");
    }
}
//炒面  12.0元
//鸡蛋炒面  13.0元
//培根鸡蛋炒面  15.0元
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
    • 就像一开始举例子的机器人,我们第二代的机器人不是在我们的第一代机器的基础上进行功能扩展,我们类似在初代机器人上套一个盒子,然后在盒子上进行功能的扩展 去掉箱子就取消对应的拓展
  • fastFood=new Egg(fastFood) 我们用的是new Egg,最后还是用fastFood去接收,这也是我们为什么要让配料去继承我们的FastFood,不然在这我们只能用配料类去接收返回的对象,而不是用传进去的fastFood对象
    • 我们本来的意思是给我们的炒面加一个鸡蛋。所以返回的应该还是我们的炒面,如果不实现继承,那么我们加个鸡蛋,最后返回了却是一个配料(鸡蛋),这是不符合我们的意图的

使用场景

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

    • 不能采用继承的情况主要有两类:

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。

    • 第二类是因为类定义不能继承(如 final 类)。

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

JDK源码分析

JDK源码解析
IO流中的包装类使用到了装饰者模式:BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。

以 BufferedWriter 举例来说明,先看看如何使用 BufferedWriter:

// 创建FileWriter对象
FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
// 创建BufferedWriter对象
BufferedWriter bw = new BufferedWriter(fw);
// 写数据
bw.write("hello Buffered");
bw.close();

使用起来感觉确实像是装饰者模式,接下来看它们的结构

在这里插入图片描述

  • InputStreamWriter是抽象构件 FileWriter是具体构件
  • BufferWriter是具体装饰角色

BufferedWriter 使用装饰者模式对 Writer 子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

静态代理和装饰者的区别

相同点:

  • 都要实现与目标类相同的业务接口

  • 在两个类中都要声明目标对象

  • 都可以在不修改目标类的前提下增强目标方法

不同点:

  • 目的不同

    • 装饰者是为了增强目标对象

      • 静态代理是为了保护和隐藏目标对象
  • 装饰者可以迭代增强,代理只能增强一次

    • 比如我们上面的例子,我们的炒饭可以加鸡蛋,然后再加一个培根
    • 而我们的代理,比如我们的日志功能,我们想要给某个类添加日志功能,我们直接进行调用代理对象进行功能增强
  • 获取目标对象构建的地方不同

    • 装饰者是由外界传递进来,可以通过构造方法传递
      • 我们的装饰着中的具体构建对象是由外部传入到我们的装配者中
    • 静态代理是在代理类内部创建,以此来隐藏目标对象
      • 我们的静态代理中的真实主题类对象是在我们的静态类内部中创建的。而不是由外部传入的

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Wi-Fi】802.11/802.11b/802.11g/802.11n/802.11a/802.11ac/802.11ax/802.11be

WiFi发展历史 IEEE 802.11 Protocol Release Date Frequency Band Bandwidth Max Throughput 802.11-1997 1997 2.4GHz 22MHz 2Mbps 802.11b 1999 2.4GHz 22MHz 11Mbps 802.11a 1999 5GHz 20MHz 54Mbps 802.11g 2003 2.4GHz 20MHz 54Mbps 802.11n (W…

计算机组成原理基础练习题第一章

有些计算机将一部分软件永恒地存于只读存储器中,称之为() A.硬件    B.软件C.固件    D.辅助存储器输入、输出装置以及外界的辅助存储器称为() A.操作系统    B.存储器 C.主机      D.外围设备完整的计算机系…

OpenCL编程指南-4.1OpenCL C编程

使用OpenCL C编写数据并行内核 OpenCL中的数据并行性表述为一个N维计算域,其中N1、2或3。N-D域定义了可以并行执行的工作项的总数。下面通过一个简单的例子来了解如何用OpenCL C编写一个数据并行内核,将两个浮点数数组相加。这个代码的串行版本求和时需…

力扣19删除链表的倒数第 N 个结点:思路分析+图文全解+方法总结(快慢指针法递归法)+深入思考

文章目录 第一部分:题目描述第二部分:代码实现2.1 快慢指针法2.2 递归 第一部分:题目描述 🏠 链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) ⭐ 难度:中等 第二部分&#…

lol由于找不到vcruntine140_1.dll文件,vcruntime140_1.dll修复方法

家人们有没有遇到过打开游戏或者软件提示由于找不到vcruntime140_1.dll,无法继续执行此代码的情况,是不是不知道怎么修复呢?Vcruntime140_1.dll是一个Windows系统文件,它是Microsoft Visual C Redistributable for Visual Studio …

【嵌入式环境下linux内核及驱动学习笔记-(11-设备树)】

目录 1、设备树体系1.1 DTS /DTSI / DTC / DTB 2、基础语法2.1 节点语法2.1.1 通用名称建议 2.2 属性语法2.2.1 属性值 2.3 关于label2.4 节点的[unit-address] 与reg属性2.5 根节点 /2.6 标准属性compatible2.6.1 of_machine_is_compatible函数 2.7 地址编码2.7.1 标准属性#ad…

RabbitMQ养成记 (3.MQ的简单工作模式 和 Pub/sub 订阅模式)

上一篇是一个简单的helloworld。 我们直接发直接收 这种是最简单的。 下面我们再来接触更加复杂一点: 简单工作模式 work queues 工作队列模式: 这里注意 这里的消息 对两个消费者 c1 c2来说是竞争关系 而不是等份分发关系, 就像两个线程…

JVM 方法区

栈、堆、方法区的交互关系 线程共享角度: 新建对象分配: 方法区的理解 方法区(Method Area) 与 Java 堆一样,是各个线程共享的内存区域方法区在 JVM 启动的时候被创建,并且它的实际物理内存空间中和 Java 堆区一样都可以不连续的方法区的大小&#xf…

HNU-操作系统OS-实验Lab5

OS_Lab5_Experimental report 湖南大学信息科学与工程学院 计科 210X wolf (学号 202108010XXX) 实验目的 了解第一个用户进程创建过程了解系统调用框架的实现机制了解ucore如何实现系统调用sys_fork/sys_exec/sys_exit/sys_wait来进行进程管理 实验…

1099 Build A Binary Search Tree(超详细注解+38行代码)

分数 30 全屏浏览题目 作者 CHEN, Yue 单位 浙江大学 A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties: The left subtree of a node contains only nodes with keys less than the nodes key.The right subtree…

使用云服务器可以做什么?十大使用场景举例说明

使用阿里云服务器可以做什么?阿里云百科分享使用阿里云服务器常用的十大使用场景,说是十大场景实际上用途有很多,阿里云百科分享常见的云服务器使用场景,如本地搭建ChatGPT、个人网站或博客、运维测试、学习Linux、跑Python、小程…

详解c++STL—string组件

目录 一、string基本概念 1、本质 2、string和char * 区别: 3、特点: 二、string构造函数 1、构造函数原型 2、示例 三、string赋值操作 1、赋值的函数原型 2、示例 四、string字符串拼接 1、函数原型 2、示例 五、string查找和替换 1、功…

2023系统分析师---软件工程、系统规划高频错题

系统规划---成本效益分析 评价信息系统经济效益常用的方法主要有成本效益分析法,投入产出分析法和价值工程方法。盈亏平衡法常用于销售定价; 可行性分析 系统规划是信息系统生命周期的第一个阶段,其任务是对企业的环境、目标以及现有系统的状况进行初步调查,根据企业目标…

【利用AI让知识体系化】万字深入浅出Nginx

思维导图 文章目录 思维导图 第一部分:入门篇1.1 起步下载和安装Nginx启动NginxNginx配置文件Nginx命令行总结 1.2 Nginx的基本架构1.3 安装和配置Nginx1.4 Nginx的基本操作 第二部分:核心篇2.1 Nginx的请求处理2.2 Nginx的缓存机制2.3 Nginx的负载均衡机…

题解校验码—CRC循环校验码与海明校验码

码距 一个编码系统的码距是任意两个码字的最小距离。 例如个编码系统采用三位长度的二进制编码,若该系统有四种编码分别为:000,011,100,111,此编码系统中000与111的码距为3;011与000的码距为2…

Hard Patches Mining for Masked Image Modeling

摘要 蒙面图像建模(MIM)因其在学习可伸缩视觉表示方面的潜力而引起了广泛的研究关注。在典型的方法中,模型通常侧重于预测掩码补丁的特定内容,并且它们的性能与预定义的掩码策略高度相关。直观地说,这个过程可以被看作…

WiFi(Wireless Fidelity)基础(九)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

【GAMES101】作业2学习总结

本系列博客为记录笔者在学习GAMES101课程时遇到的问题与思考。 GAMES101:课程官网GAMES101:B站视频GAMES101:相关文件下载(百度网盘) 一、基础题 本次作业的目的是为了让我们熟悉三角形栅格化的相关操作,通过Assignment2.pdf可以…

白嫖chatgpt的Edge插件,很难不爱啊

目录 🍁1.常见的Edge浏览器界面 🍁二.安装WebTab插件 🍁三.WebTab插件的各种功能 🍁1.支持免费的chatgpt,不限次数​编辑 🍁2.有几个休闲的小游戏可以玩耍,点击即玩。 🍁3.支…

618前夕,淘宝天猫大变革,探索电商天花板之上的价值

2023年淘宝天猫618商家大会,恰逢淘宝20周年,也是阿里“16N”组织架构改革,淘宝天猫“独立”经营后,管理和运营团队的首次亮相。除了淘宝天猫618的具体策略,最受关注的,还有淘宝天猫的大变革——涉及淘宝天猫…