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

设计模式之装饰模式--优雅的增强

目录

  • 概述
    • 什么是装饰模式
    • 为什么使用装饰模式
    • 关键角色
    • 基本代码
    • 应用场景
  • 版本迭代
    • 版本一
    • 版本二
    • 版本三—装饰模式
  • 装饰模式中的巧妙之处
    • 1、被装饰对象和装饰对象共享相同的接口或父类
    • 2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法
    • 3、子类方法与父类方法共享相同的引用
    • 4、装饰模式与继承的对比
  • 总结

概述

什么是装饰模式

    装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象放入包含行为的特殊封装对象中来为原始对象添加新的行为。装饰模式在不改变原始对象接口的情况下,动态地将责任附加到对象上。就增加功能来看,装饰模式比生成子类更为灵活。

为什么使用装饰模式

    使用装饰模式有以下几个优点:

灵活性:装饰模式允许在运行时动态地给对象添加功能,而不需要修改其代码或使用继承。这使得系统更加灵活,易于扩展和修改。
单一职责原则:装饰模式能够将不同的功能划分到不同的类中,使每个类只负责单一的功能。这遵循了单一职责原则,使得代码更加清晰、可读性更高。
可组合性:由于装饰模式使用了对象组合而不是继承,因此可以通过不同的方式将装饰器组合起来,以获得不同的行为组合。
装饰模式的结构

关键角色

  • 抽象组件(Component):定义了原始对象和装饰器的公共接口,可以是接口或抽象类。
  • 具体组件(Concrete Component):实现了抽象组件接口,是被装饰的原始对象。
  • 装饰器(Decorator):实现了抽象组件接口,并持有一个抽象组件对象的引用,在装饰器中可以添加一些额外的行为。
    -具体装饰器(Concrete Decorator):继承自装饰器,具体实现了在装饰器中定义的额外行为。

基本代码

应用场景

1、在不改变现有对象结构的情况下,对对象的功能进行扩展或修改。
2、需要动态地给对象添加功能,以便根据需要增加或移除功能。
3、对象的职责应该能够被多次扩展,而不会导致类的数量急剧增加。

版本迭代

需求:给人搭配不同的服饰

版本一

//Person
public class Person {private String name;public Person(String name ){this.name=name;}public void wearTShirts(){System.out.println("大T恤");}public void wearBigTrouser(){System.out.println("跨裤");}public void wearSneakers(){System.out.println("球鞋");}public void show(){System.out.println("装扮的"+name);}
}
//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");xc.wearTShirts();xc.wearBigTrouser();xc.wearSneakers();xc.show();System.out.println("--------------");System.out.println("第二种装扮");xc.wearTShirts();xc.wearSneakers();xc.show();}
}

    版本一非常简单,给人穿衣服就只有Person这个类,但是需求如果需要增加“超人”的装扮,就需要修改Person类(毕竟超人不是人,不能使用实例化Person,并且超人的衣服最起码是有个内裤和斗篷的~~)
    这个时候怎么办,把服饰拿出去作为单独的类是不是就可以了呢

版本二

//Person
public class Person {private String name;public Person(String name){this.name=name;}public void show(){System.out.println("装扮的"+name);}
}//抽象服饰类
public abstract class Finery {public abstract void show();
}//具体的服饰类
public class BigTrouser extends Finery{@Overridepublic void show() {System.out.println("跨裤");}
}public class TShirts extends Finery{@Overridepublic void show() {System.out.println("大t恤");}
}//客户端
public class client {public static void main(String[] args) {Person xc=new Person("小蔡");Finery tt=new TShirts();Finery kk=new BigTrouser();kk.show();tt.show();xc.show();}
}

    新的问题好像又来了,客户端看起来就是光着身子的小蔡一件件的穿上了衣服,也就是说穿衣服的过程不应该在客户端显示,而应该在内部完成,并且每个人的爱好不一样,穿衣服的顺序并不确定,这个建造者就不同了,而是需要把所有的功能按照正确顺序串联起来进行控制。装饰模式上场了

版本三—装饰模式

//服饰抽象类
public interface ICharacter {public void show();
}//被装饰对象人
public class Person implements ICharacter{private String name;public Person(String name){this.name=name;}@Overridepublic void show() {System.out.println("装扮的"+name);}
}
//装饰类
public class Finery implements ICharacter{protected ICharacter component;public void decorate(ICharacter component){this.component=component;}@Overridepublic void show() {if(component !=null){component.show();}}
}//具体装饰类
public class Sneakers extends Finery{public void show(){System.out.println("球鞋");super.show();}}public class BigTrouser extends Finery{public void show(){System.out.println("裤子");super.show();}}public class Tshirts extends Finery{public void show(){System.out.println("T恤");super.show();}
}//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");Sneakers pqx=new Sneakers();pqx.decorate(xc);BigTrouser kk=new BigTrouser();kk.decorate(pqx);Tshirts tt=new Tshirts();tt.decorate(kk);tt.show();}
}

装饰模式中的巧妙之处

1、被装饰对象和装饰对象共享相同的接口或父类

    这样做的目的是是为了让它们可以互相替代。这种互相替代的能力是装饰模式的关键特点之一。通过共享相同的接口或父类,可以在不修改原始对象的情况下,动态地添加、删除或更改对象的行为。

    互相替换的好处在于灵活性和扩展性。当需要为对象添加额外的功能时,可以直接用装饰对象替换原始对象,而不需要修改原始对象的代码。可以将装饰器对象看作是被装饰对象的 “包装”。这样可以避免引入大量的条件语句或继承关系,使得代码更加清晰、可维护和可扩展。

    另外,互相替换还可以实现功能的组合和嵌套,比如上述给人装扮的例子,穿了靴子和裤子,就是把靴子和裤子的功能进行了组合。通过将装饰器对象嵌套在其他装饰器对象中,可以构建出复杂的功能组合,以满足不同的需求。

2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法

    例如上面装扮的例子中的show方法.装饰器类(服饰类)通过持有一个被装饰对象(component)的引用来添加额外的功能。当调用装饰器类的 show() 方法时,它会先调用被装饰对象的 show()方法,然后再执行自己的装饰逻辑(如下图所示)。这样的设计可以实现动态地扩展对象的功能,而无需改变原始对象的结构。通过嵌套和组合多个装饰器类灵活地添加、移除和组合各种功能,以满足不同的需求。
在这里插入图片描述

3、子类方法与父类方法共享相同的引用

    在具体装饰子类中都有super.show方法,这个是调取父类的show方法
在这里插入图片描述
    那么这样写的巧妙之处在哪呢,在这里是看不到2中说到的“”当调用装饰器类的 show() 方法时,它会先用被装饰对象的 show()方法,然后再执行自己的装饰逻辑。因为这个逻辑写到了父类中
在这里插入图片描述
    在这里有个疑问,在具体的子类中component为什么肯定是它的上一级被装扮对象呢,比如“穿了裤子的小蔡”接着要穿T恤。这就是因为每次调用装扮对象的decorate方法,传入的都是上一级被装扮对象(如下图所示),同时子类没有重写decorate方法,此时子类方法将与父类方法共享相同的引用,并且它们将具有相同的实现和行为,也就把component传给装备对象的属性,以供后面调用show方法时使用。
在这里插入图片描述

4、装饰模式与继承的对比

    装饰模式将功能添加到对象上的方式与继承不同。使用继承时,子类会继承父类的行为,而装饰模式可以动态地将额外的行为添加到对象上,而不需要继承。这使得装饰模式更加灵活,因为可以在运行时选择不同的行为组合。

    另外,装饰模式遵循了开放-关闭原则,即对扩展开放,对修改关闭。通过组合和委托的方式,可以动态地将功能添加到对象上,而不需要修改现有代码

总结

    装饰模式是一种灵活且可扩展的设计模式,它可以动态地给对象添加新的行为。使用装饰模式可以避免类爆炸问题,并能够在运行时选择不同的功能组合。它与继承相比更加灵活,符合单一职责原则,并遵循开放-关闭原则。

相关文章:

设计模式之装饰模式--优雅的增强

目录 概述什么是装饰模式为什么使用装饰模式关键角色基本代码应用场景 版本迭代版本一版本二版本三—装饰模式 装饰模式中的巧妙之处1、被装饰对象和装饰对象共享相同的接口或父类2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法3、子类方法与父类方法…...

前端vue,后端springboot。如何防止未登录的用户直接浏览器输入地址访问

前端,使用Vue框架来实现前端路由拦截: 设置需要登录校验的页面: 登录成功后,去设置LocalStorage里面的IsLogin为true:...

linux安装Chrome跑web自动化

添加 Chrome 源: 打开终端并执行以下命令,将 Google Chrome 的 APT 源添加到系统: bashCopy code wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 安装 Chrome: 执行以下命令来安装 Chrome&…...

linux环境下编译,安卓平台使用的luajit库

一、下载luajit源码 1、linux下直接下载: a、使用curl下载:https://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz b、git下载地址;https://github.com/LuaJIT/LuaJIT.git 2、Windows下载好zip文件,下载地址:https…...

indexedDB笔记

indexedDB 该部分内容主要源于https://juejin.cn/post/7026900352968425486 常用场景:大量数据需要缓存在本地重要概念 仓库objectStore:类似于数据库中的表,数据存储媒介索引index:索引作为数据的标志量,可根据索引获…...

系统提示缺少或找不到emp.dll文件的详细解决方案

我今天打开一款《游戏》。然而,在游戏中遇到了一个非常棘手的问题:游戏报错找不到emp.dll,无法继续执行代码。这让我们非常苦恼,因为这个问题严重影响了我们的游戏体验。 在经过一番努力之后,我终于找到了4个解决方法&#xff0c…...

Python实现自动化网页操作

1 准备 推荐使用Chrome浏览器 1.1 安装selenium程序包 激活虚拟环境,打开新的Terminal,输入以下代码: python -m pip install selenium 如下图所示,表示安装成功,版本为4.7.2 安装成功 关闭虚拟环境,打…...

03 矩阵与线性变换

矩阵与线性变换 线性变换如何用数值描述线性变换特殊的线性变换反过来看总结 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 线性变换 如果一个变换具有以下两个性质,我们就称它是线性的: 一是直线在变换后仍然保持为直线二是原点必须…...

MySQL InnoDB数据存储结构

1. 数据库的存储结构:页 索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说是存储在页结构中。另一方面,索引是在存储引擎中实现的,MySQL服务器上的存储引擎负责对表中数据的读…...

【数据结构】数组和字符串(十五):字符串匹配2:KMP算法(Knuth-Morris-Pratt)

文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作4.3.3 模式匹配算法0. 朴素模式匹配算法1. ADL语言2. KMP算法分析3. 手动求失败函数定义例1例2例3 4. 自动求失败函数(C语言)5. KMP算法(C语言)6. 失败函数答案…...

STM32 PWM可控制电压原理

PWM可控制电压原理 主要通过PWM 输入模式根据控制单位时间内输出的平均电压,以调节电压大小。而PWM输出模式通过调节占空比,控制平均电压大小; 设置TIM为PWM输出模式 第一步:时钟使能: GPIO,TIM; 第二步&a…...

angular、 react、vue框架对比

借鉴:Web前端开发:三大主流框架 (baidu.com) AngularReactVue公司ChromeFaceBook尤雨溪写法有指令、模板的概念比较灵活,没有要求使用特定的架构和模式有指令和模板的概念性能低有虚拟Dom,性能高有虚拟Dome,性能高学习门槛 高&am…...

GNSS常用数据源汇总

本文整理汇总了GNSS数据处理过程中常用的数据源,路径中的占位符具体含义如下: -YYYY-年-YY-年的后两位数-DOY-年积日-MM-月-HH-小时-WWWW-GPS周 一、RINEXO观测值与RINEXN星历小时文件 1、CDDIS:ftp://gdc.cddis.eosdis.nasa.gov/pub/gnss…...

01|LangChain | 从入门到实战-介绍

​ ​ by:wenwenc9 一、基本知识储备 1、什么是大模型,LLM? 大模型(Large Language Model)是近年来一个很热门的研究方向。 使用大量的数据训练出一个非常大的模型。一般是数十亿到上万亿的参数规模。 这些大模型可以捕捉到非常复杂的语言…...

【小白专用】PHP基本语法 23.11.04

PHP基本语法 PHP是超文本预处理器 由服务器解析执行 可以与 html 进行混编(嵌入) ,PHP是一种弱类型语言 1.1 PHP标记 PHP和其他Web语言一样,都是用一对标记将PHP代码包含起来,以便和HTML代码区分开来。PHP支持4种风格的标记,如表所示。 标…...

路由器基础(七):NAT原理与配置

一、NAT 配置 华为路由器配置NAT 的方式有很多种,考试中可能考到的基本配置方 式主要有EasyIP和通过NAT地址池的方式。图22-7-1是一个典型的通过EasyIP进行NAT的示意图,其中Router出接口GE0/0/1的IP地址为200.100.1.2/24,接口E0/0/1的IP地址为192.168.0.…...

Spring Boot 整合SpringSecurity和JWT和Redis实现统一鉴权认证

📑前言 本文主要讲了Spring Security文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 🌄每日一句:努力…...

交换机基础(零):交换机基础配置

一、华为设备视图 常用视图 名称 进入视图 视图功能 用户视图 用户从终端成功登录至设备即进 入用户视图,在屏幕上显示 kHuawei> 用户可以完成查看运行状态和统 计信息等功能。在其他视图下 都可使用return直接返回用户视 图 系统视图 在用户视图下&…...

02 线性组合、张成的空间与基

线性组合、张成的空间与基 基向量缩放向量并相加给定向量张成的空间线性相关与线性无关空间的基 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 基向量 当看到一对描述向量的数时,比如[3,-2]时,把这对数中的每个数(坐标&…...

解析mfc100u.dll文件丢失的修复方法,快速解决mfc100u.dll问题

在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“缺少某个文件”的错误。最近,我也遇到了一个这样的问题,那就是“mfc100u.dll丢失”。这个问题可能会导致某些应用程序无法正常运行,给我们带来困扰。…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...