【设计模式】结构型模式·装饰者模式

news2024/12/23 10:19:29

学习汇总入口【23种设计模式】学习汇总(数万字讲解+体系思维导图)
写作不易,如果您觉得写的不错,欢迎给博主来一波点赞、收藏~让博主更有动力吧!

一.概述

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

【例】

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

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

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

    • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。

    • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。

    • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

    • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

二.使用

(1) 案例

我们使用装饰者模式对快餐店案例进行改进。

类图如下:
在这里插入图片描述

代码如下:

//快餐接口
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 void setPrice(float price) {
        this.price = price;
    }

    public float getPrice() {
        return price;
    }

    public String getDesc() {
        return desc;
    }

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

    public abstract float cost();  //获取价格
}

//炒饭
public class FriedRice extends FastFood {

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

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

//炒面
public class FriedNoodles extends FastFood {

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

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

//配料类
public abstract class Garnish extends FastFood {

    private FastFood fastFood;

    public FastFood getFastFood() {
        return fastFood;
    }

    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }

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

//鸡蛋配料
public class Egg extends Garnish {

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

    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

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

//培根配料
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {

        super(fastFood,2,"培根");
    }

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

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

//测试类
public class Client {
    public static void main(String[] args) {
        //点一份炒饭
        FastFood food = new FriedRice();
        //花费的价格
        System.out.println(food.getDesc() + " " + food.cost() + "元");

        System.out.println("========");
        //点一份加鸡蛋的炒饭
        FastFood food1 = new FriedRice();

        food1 = new Egg(food1);
        //花费的价格
        System.out.println(food1.getDesc() + " " + food1.cost() + "元");

        System.out.println("========");
        //点一份加培根的炒面
        FastFood food2 = new FriedNoodles();
        food2 = new Bacon(food2);
        //花费的价格
        System.out.println(food2.getDesc() + " " + food2.cost() + "元");
    }
}

好处:

  • 装饰者模式可以带来比继承更加灵活性的扩展功能,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。

  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

(2) 使用场景

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

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

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
    • 第二类是因为类定义不能继承(如final类)
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

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

(3) 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子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

(4) 代理和装饰者的区别

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

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

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

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

相关文章

2022年房地产投资退出方法和工具研究报告

第一章 房地产投资概况 房地产商品既是人们日常“衣食住行”中的一种必需品,又因保值增值的功能而具有很好的投资品属性。房地产投资是以房地产为对象,为获得预期效益而对土地和房地产开发、房地产经营,以及购置房地产等进行的投资。 房地产…

深度学习 GNN图神经网络(二)PyTorch Geometric(PyG)安装

一、前言 我们使用torch_geometric库来实现图神经网络的编码,因为它与PyTroch天然集成。本文介绍了PyTorch Geometric(PyG)的安装与测试。 二、安装 首先打开官方的安装说明文档:https://pytorch-geometric.readthedocs.io/en/…

蓝桥杯 stm32 LCD显示及 数据格式化

文章代码使用 HAL 库。 文章目录前言一、LCD 原理图:二、LCD 基本函数:1.LCD 清屏函数:LCD_Clear ( u16 Color );2. 显示一行字符串:LCD_DisplayStringLine(u8 Line, u8 *ptr);3.设置字符背景色&#xff1a…

switch自制软件开发环境搭建

参考: https://switch.homebrew.guide/ https://switchbrew.org/wiki/Main_Page https://www.bilibili.com/video/BV133411Q77X/?spm_id_from333.788&vd_sourcec5c272e9490d8bf475c8204462fc26e7 1.开发环境 开发机 -> 虚拟机 ubuntu22.04 设备 -> 破解switch 大…

Ubuntu20.04系统WineHQ7.0安装微信

提供3种Ubuntu系统安装微信的方法,在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信:ubuntu20.04安装最新版微信--可以支持微信最新版,但是适配的不是特别好;比如WeChartOCR.exe 报错。2. 原生微信安装:linux系统下的微信…

[电商实时数仓] 数据仓库建模过程分析

文章目录1.数据仓库概述1.1 数据仓库概念1.2 数据仓库核心架构2.数据仓库建模概述2.1 数据仓库建模的意义2.2 数据仓库建模方法论2.2.1 ER模型2.2.2 维度模型3.维度建模理论之事实表3.1 事实表概述3.2 事实表分类3.3 事务事实表4.维度建模理论之维度表5.数据仓库设计5.1 数据仓…

[前端笔记——HTML 表格] 8.HTML 表格

[前端笔记——HTML 表格] 8.HTML 表格1.HTML 表格基础1.1 什么是表格&#xff1f;1.2 创建表格&#xff1a;2.HTML 表格高级特性和无障碍2.1 使用<caption>为表格增加一个标题2.2 添加<thead>,<tfoot>和<tbody>结构2.3 嵌套表格2.4 对于视力受损的用户…

第五层:C++中的运算符重载

文章目录前情回顾运算符重载概念为什么会出现运算符重载运算符重载中函数名格式加减运算符重载作用实现左移运算符重载作用左移运算符是什么&#xff1f;实现递增递减运算符作用实现前置后置赋值运算符重载关系运算符重载作用实现函数调用运算符重载第二种重载掌握&#xff01;…

vueJs中toRaw与markRaw函数的使用比较

01toRaw()函数接收一个reactive响应式数据,将一个响应式的数据变为普通类型的数据,转化为非响应式数据,相当于还原对象,reactive相当于制作,但对于ref响应式数据不起作用将一个由reactive生成的响应式对象转为普通(原始)对象toRaw()可以返回由reactive(),readonly(),shallowRea…

Java_Git:1. Git简介

目录 1 Git历史 2 Git与Svn对比 2.1 Svn特点 2.2 Git特点 3 Git工作流程 4 Git的安装 4.1 软件下载 4.1.1 git 4.1.2 tortoisegit 4.2 软件安装 4.2.1 安装git for windows 4.2.2 安装TortoiseGit 4.2.3 安装TortoiseGit中文语言包 1 Git历史 版本控制系统目标&…

Spread 16.0.2 for Winforms Crack-2023.1.4 Version

Spread使用这些无依赖性的 WinForms 电子表格组件探索 WinForms 企业应用程序的可能性。 Spread新增&#xff1a;v15 NuGet 包现在支持 .NET 6.0、.NET Core 3.1 和 .NET 4.62 使用桌面设计器应用程序快速提供类似 Excel 的电子表格体验 使用全面的 API创建企业电子表格、网格…

【Python-Django】医疗辅助平台-创建项目-day1

前期准备请参考此文: https://codeknight.blog.csdn.net/article/details/126780724https://codeknight.blog.csdn.net/article/details/126780724下载BootStrap插件: Bootstrap v3 中文文档 Bootstrap 是最受欢迎的 HTML、CSS 和 JavaScript 框架,用于开发响应式布局、移…

字符串匹配算法详解

为保证代码严谨性&#xff0c;文中所有代码均在 leetcode 刷题网站 AC &#xff0c;大家可以放心食用。皇上生辰之际&#xff0c;举国同庆&#xff0c;袁记菜馆作为天下第一饭店&#xff0c;所以被选为这次庆典的菜品供应方&#xff0c;这次庆典对于袁记菜馆是一项前所未有的挑…

excel图表美化:用散点标记制作不一样的折线图

柱形图常常用于显示一段时间内的数据变化或显示各项之间的比较情况。但当时间序列过多时&#xff0c;我们往往考虑用折线图来反映数据的变化趋势。之所以讲这个&#xff0c;是希望大家能够把折线图和柱形图的应用区分开来&#xff0c;根据自己的需求使用不同的图表。以下是各个…

深度学习 GNN图神经网络(一)图的基本知识

一、前言 本文主要介绍图的一些基础知识&#xff0c;不会太深奥&#xff0c;够用就行。我们以民国最出名的七角恋人物关系图为例进行讲解。 二、图的概念 图&#xff08;Graph&#xff09;可以用来描述实体之间的关系。 如下图所示&#xff0c;一张图捋清民国最出名的七角恋…

DW动手学数据分析Task5:数据建模及模型评估

目录1 建模1.1 数据分析流程1.2 模型搭建准备工作1.2.1 导入库1.2.2 载入数据1.3 模型搭建1.3.1 选择模型1.3.2 切割训练集和测试集1.3.3 模型创建1.3.4 输出模型预测结果2 评估2.1 评估的准备工作2.2 模型评估2.2.1 交叉验证2.2.2 混淆矩阵2.2.3 ROC曲线1 建模 1.1 数据分析流…

Mac创建python2虚拟环境

前提&#xff1a;已经安装配置好python2.7版本&#xff0c;使用python和pip命令可以得到如下返回信息 1.安装virtualenv和virtualenvwrapper pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple sudo pip install virtualenvwrapper -i https://pypi.tuna.t…

1595_AURIX_TC275_PMU_应用提示2

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 如果通过标注来标注了异常字行&#xff0c;那么在算法设计的时候&#xff0c;检查到之后应该跳过这一行的数据。 可以纠正的ECC在PFlash中是可以忽略的&#xff0c;相应的信息只是可以用来…

【SpringCloud】Ribbon负载均衡的基本原理与使用

【SpringCloud】Ribbon负载均衡的基本原理与使用 一、负载均衡原理 二、源码解析 LoadBalanced IDEA源码跟踪 负载均衡源码小结 三、负载均衡策略 负载均衡策略 策略规则解析 自定义负载均衡策略 &#xff08;1&#xff09;代码方式 &#xff08;2&#xff09;配置文…

Unity的Bounds(包围盒)简记

Unity的Bounds&#xff08;包围盒&#xff09;简记一、Bounds(包围盒)概述1.什么是包围盒?2.包围盒的类型2.1 AABB包围盒(Axis-aligned bounding box)2.2 包围球(Sphere)2.3 OBB方向包围盒(Oriented bounding box)2.4 FDH固定方向凸包(Fixed directions hulls或k-DOP)2.5 包围…