结构型模式-装饰器模式

news2024/9/23 13:21:18

1.概述

快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。

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

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

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

2.结构

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

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

3.案例

我们使用装饰者模式对快餐店案例进行改进,体会装饰者模式的精髓。
类图如下
在这里插入图片描述
代码如下

package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName FastFood
 * @description: 快餐类(抽象构建Component角色)
 * @author: 
 * @create: 2023-01-15 16:29
 * @Version 1.0
 **/
public abstract class FastFood {
    private float price;// 价格
    private String desc;// 描述

    public FastFood() {
    }

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

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public abstract float cost();
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName Egg
 * @description: 培根类:具体装饰(ConcreteDecorator)角色
 * @author: 
 * @create: 2023-01-15 16:40
 * @Version 1.0
 **/
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {
        super(2, "培根", fastFood);
    }

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

    @Override
    public String getDesc() {
        // 描述信息
        return super.getDesc() + getFastFood().getDesc();
    }
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName Egg
 * @description: 鸡蛋类:具体装饰(ConcreteDecorator)角色
 * @author: 
 * @create: 2023-01-15 16:40
 * @Version 1.0
 **/
public class Egg extends Garnish {

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

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

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName FriedNoodles
 * @description: 炒面:具体构件(Concrete Component)角色
 * @author: 
 * @create: 2023-01-15 16:35
 * @Version 1.0
 **/
public class FriedNoodles extends FastFood {

    public FriedNoodles() {
        super(11, "炒面");
    }

    @Override
    public float cost() {
        return getPrice();
    }
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName FriedRice
 * @description: 炒饭:具体构件(Concrete Component)角色
 * @author: 
 * @create: 2023-01-15 16:32
 * @Version 1.0
 **/
public class FriedRice extends FastFood {

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

    @Override
    public float cost() {
        return getPrice();
    }
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName Garnish
 * @description: 装饰者类:抽象装饰(Decorator)角色
 * @author: 
 * @create: 2023-01-15 16:37
 * @Version 1.0
 **/
public abstract class Garnish extends FastFood {
    // 声明快餐类的变量
    private FastFood fastFood;

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

    public FastFood getFastFood() {
        return fastFood;
    }

    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }
}
package com.itheima.pattern.decorator;

/**
 * @program: design-patterns
 * @ClassName Client
 * @description: 测试类
 * @author: 
 * @create: 2023-01-15 16:45
 * @Version 1.0
 **/
public class Client {
    public static void main(String[] args) {
        FastFood rice = new FriedRice();
        System.out.println(rice.getDesc() + " " + rice.cost() + "元");
        System.out.println("====================");
        // 在上面的炒饭中加一个鸡蛋
        FastFood rice1 = new Egg(rice);
        System.out.println(rice1.getDesc() + " " + rice1.cost() + "元");

        System.out.println("====================");
        // 在上面的炒饭中加一份培根
        FastFood bacon = new Bacon(rice);
        System.out.println(bacon.getDesc() + " " + bacon.cost() + "元");
    }
}

好处:

  • 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

4.使用场景

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

5.JDK源码解析

IO流中的包装类使用到了装饰者模式。BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。
我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter

public class Demo {
	public static void main(String[] args) throws Exception{
		//创建BufferedWriter对象
		//创建FileWriter对象
		FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
		BufferedWriter bw = new BufferedWriter(fw);
		//写数据
		bw.write("hello Buffered");
		bw.close();
	}
}

使用起来感觉确实像是装饰者模式,接下来看它们的结构:
在这里插入图片描述

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

6.代理和装饰者的区别

静态代理和装饰者模式的区别:

  • 相同点
    (1)都要实现与目标类相同的业务接口
    (2)在两个类中都要声明目标对象
    (3)都可以在不修改目标类的前提下增强目标方法
  • 不同点
    (1)目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象
    (2)获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象

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

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

相关文章

Alibaba微服务组件Sentinel学习笔记

1 .Sentinel 是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以 流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的…

一篇读懂图神经网络

来源:投稿 作者:张宇 编辑:学姐 近年来,作为一项新兴的图数据学习技术,图神经网络(GNN)受到了非常广泛的关注,在各大顶级学术会议上,图神经网络相关的论文也占了相当可观…

https://app.diagrams.net/在线画图的一些技巧

最近工作需要,实践了在线画图的case, 下面就把使用心得记录一下: 关于diagrams 的一些小技巧: 登入的网页是:Flowchart Maker & Online Diagram Software 1: 利用group 的选项,这个可以整体移动,不用担心会漏掉一个: 就是选中一个图标,然后,看右边arrange 下面…

20230123使AIO-3568J开发板在Android12下永不休眠

20230123使AIO-3568J开发板在Android12下永不休眠 2023/1/23 13:59 1、 Z:\android12-rk3568-new\device\rockchip\common\device.mk # Bluetooth HAL PRODUCT_PACKAGES \ libbt-vendor \ android.hardware.bluetooth1.0-impl \ android.hardware.bluetooth1.0-se…

Hadoop基础之《(1)—大数据基本概念》

一、Hadoop 1、Hadoop大数据框架,处理分布式环境下数据存储和计算 2、Hadoop的HDFS处理存储 3、Hadoop的MapReduce处理计算 map让任务数据拆分到每一台去执行 reduce处理后的任务合并 4、Hive作用是在Hadoop上能够让用户来写SQL处理数据 Hive的执行引擎,…

深度学习TensorFlow—GPU2.4.0版环境配置,一文简单易懂详细大全,CUDA11.0、cuDNN8.0

深度学习TensorFlow—GPU2.4.0版环境配置,一文简单易懂详细大全,CUDA11.0、cuDNN8.0 前提:电脑拥有英伟达独立显卡!!!,并且安装了anaconda!!! 前提:电脑拥有英伟达独立显卡!!!&…

vue事件车之兄弟组件之间传值

目录前言一,全局事件总线介绍1.1 原理介绍1.2 x需要满足的条件二,知识点的复习2.1 vc是什么2.2 vm管理vc如何体现2.3 原型2.4 上述知识的串联三,实现需求3.1 x的编写及讲解3.2 使用x四,标准写法4.1 写法改动4.2 销毁五 关键代码后…

兔年首文迎新春-Cesium橘子洲烟花礼赞

兔年新春今天是兔年大年初二,神州大地,在经历了疫情的三年后迎来开放的一个春节。大家都沉浸在欢乐幸福的春节气氛中。玉兔迎新春,祝福齐送到:白兔祝你身体安康,黑兔祝你薪水高涨,灰兔送你梦想如意&#xf…

Maven高级

Maven高级 1,分模块开发 1.1 分模块开发设计 (1)按照功能拆分 我们现在的项目都是在一个模块中,比如前面的SSM整合开发。虽然这样做功能也都实现了,但是也存在了一些问题,我们拿银行的项目为例来聊聊这个事。 网络没有那么发…

Java多线程03——等待唤醒机制(and阻塞队列实现)

目录1.等待唤醒机制1.ThreadDemo2.Desk3.Cook4.Foodie2.等待唤醒机制(阻塞队列方式实现)1.ThreadDemo022.Cook023.Foodie023.线程的状态1.等待唤醒机制 生产者和消费者 桌子上有食物,消费者吃,桌子上没有食物,消费者等…

ElasticSearch 索引模板 组件模板 组合模板详细使用介绍

索引模板_template 文章目录索引模板_templateTemplate 介绍索引模板Index Template参数说明创建一个索引模板 Index Template测试不存在的索引直接添加数据创建索引总结组合索引模板 Index Template 7.8版本之后引入创建基于组件模板的索引模板 Index Template创建组件模板模拟…

LeetCode103_ 103. 二叉树的锯齿形层序遍历

LeetCode103_ 103. 二叉树的锯齿形层序遍历 一、描述 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 示例 1&…

【头歌】顺序表的基本操作

第1关:顺序表的插入操作任务描述本关任务:编写顺序表的初始化、插入、遍历三个基本操作函数。相关知识顺序表的存储结构顺序表的存储结构可以借助于高级程序设计语言中的数组来表示,一维数组的下标与元素在线性表中的序号相对应。线性表的顺序…

YOLOv5/v7 引入 YOLOv8 的 C2f 模块

YOLOv8 项目地址:https://github.com/ultralytics/ultralytics YOLOv8 Ultralytics YOLOv8 是由 Ultralytics 开发的一个前沿的 SOTA 模型。它在以前成功的 YOLO 版本基础上,引入了新的功能和改进,进一步提升了其性能和灵活性。YOLOv8 基于快…

C 指针变量 取地址符的用法 *指针变量名的用法

文章目录IntroCode图示Intro C语言中有一类特殊的变量:指针变量(pointer variable),用于存储某个变量的内存地址的值。 要打印指针变量一般用%p格式符,会打印出该指针变量的值,即一个内存值。 Code // Created by wuyujin1997 …

【Linux】进程概念一

进程概念一 冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。 截止目前为止, 我们所认识的计算机,都是一个个的硬件组成 输入设备:包括键盘&#x…

如何使用JDBC操作数据库?JDBC API的使用详细解读

文章目录1. DriverManager1.1 注册驱动1.2 获取连接2. Connection2.1 获取执行sql的对象2.2 事务管理3. Statement4. ResultSet5. PreparedStatement5.1 sql注入问题5.2 preparedStatement 原理6. 总结Java编程基础教程系列1. DriverManager DriverManager ,驱动管…

C++模板不支持分离编译的问题

目录前言分离编译模式普通函数的分离编译(正常)模板函数的分离编译(出错)分析解决方式拓展--extern关键字extern"C"extern变量extern模板--控制实例化前言 分离编译模式 一个项目如果有多个源文件.c组成,每个源文件单独编译,形成目标文件。最…

Kubernetes:分享一个可以展示资源视图的 K8s开源 Web/桌面 客户端工具 Octant

写在前面 博文内容主要为 Octant 介绍以及 桌面/Web 端的安装教程涉及 Linux/Windows 的安装。理解不足小伙伴帮忙指正 其实当你什么都经历了,会发现,人生无论你怎么精心策划都抵不过一场命运的安排。 Octant 不是仪表板,Octant 是一个带有仪…

数据结构 最短路径课设(源码+实验报告+视频讲解)(不要钱、用了自取)

XIAN TECHNOLOGICAL UNIVERSITY 课程设计报告 实验课程名称 算法与数据结构 专 业: 班 级: 姓 名: 学 号: 实验学时: 指导…