设计模式—装饰者模式

news2024/11/15 7:04:22

一、什么是装饰者模式

装饰者模式是一种结构型设计模式,它允许你动态地向对象添加新的行为而不影响其原有的行为。它在运行时给对象动态地添加一些额外的职责,通常是在原有的行为基础上,通过装饰器进行一些修饰,实现了更加灵活的代码复用和扩充。

  • 给对象添加一些职责,但是又不想改变其原有的接口和实现。
  • 在不使用继承的情况下(避免出现由于继承关系带来的类很多问题)动态地为一个对象添加一些额外的功能。
  • 需要在程序运行时动态地为对象添加不同的功能,或者为对象添加同时使用多个的功能。

一句话,当遇到需要给某个类添加新功能,但又不能改源代码或不希望影响其他对象的情况下,可以考虑装饰者模式。

二、角色组成

在这里插入图片描述

  • Component(组件) :组件接口 或 抽象类 定义了 组件实现类 和 装饰器实现类 的行为。
  • ConcreteComponent(组件实现类) :组件实现类 实现了 Component 接口(或抽象类)。通常情况下,具体组件实现类就是被装饰器装饰的原始对象。
    该类提供了 Component 接口中定义的最基本的功能,其他高级功能或后序添加的新功能都是通过装饰器的方式添加到该类的对象之上的。
  • Decorator(装饰器) :装饰器,它是一个实现了 Component 接口的类,并包含一个 Component 类型的成员变量,表示 被装饰 的对象。它的构造函数可以接收一个Component类型的参数,用于初始化成员变量。
  • ConcreteDecorator(装饰器的实现类) :该实现类要向 被装饰对象 添加某些功能 。

三、场景

生活场景

  1. 穿衣服:我们每天都要穿衣服,根据不同的场合和气温选择不同的服装,比如外套、围巾、帽子等,这些都是具体装饰器(Concrete Decorator),而我们就是被装饰的抽象组件(Component)。
  2. 手机配件:如手机壳、钢化膜等。都可以看作是装饰者模式的应用,手机就是抽象组件(Component),手机壳、钢化膜等都是具体装饰器(Concrete Decorator)。
  3. 电影特效:如烟雾、爆炸、光影等,影片为抽象组件(Component),各种特效就是具体装饰器(Concrete Decorator)。

java场景

  1. IO流的处理:这是一个典型的装饰者模式的应用。InputSteam和OutputStream是最基本的抽象组件(Component),而各种FilterInputSteam和FilterOutputStream就是具体的装饰器,它们可以实现各种不同的IO流处理功能,如缓冲、压缩、加密等等。
  2. 数据库连接池:连接池为抽象组件(Component),各种不同的连接池实现(如C3P0、DBCP等)则是具体的装饰器(Concrete Decorator),它们可以实现不同的连接池缓存策略、连接池大小、超时时间等属性。
  3. Spring的AOP:在AOP中,切面就是具体的装饰器(Concrete Decorator),而业务逻辑则是抽象组件(Component),通过动态代理技术,将具体的业务逻辑和切面对象组合起来,我们就可以实现在不修改源码的情况下,动态地为业务逻辑添加新的功能。

四、代码示例

2.1、示例 UML图

在这里插入图片描述

2.2、组件接口类 Drink
public abstract class Drink {

	public String des; // 描述
	private float price = 0.0f;
	
	public void setDes(String des) {
		this.des = des;
	}
	
	public String getDes() {
		return des ;
	}
	
	
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	
	//计算费用的抽象方法
	//子类来实现
	public abstract float cost();	
}
2.3、定义组件类 Coffee
public class Coffee  extends Drink {

	@Override
	public float cost() {
		return super.getPrice();
	}
}
2.4、组件子类 Espresso、CoffeeLongBlack
public class CoffeeEspresso extends Coffee {	
	public CoffeeEspresso() {
		setDes(" 意大利咖啡 ");
		setPrice(6.0f);
	}
}


public class CoffeeLongBlack extends Coffee {
	public CoffeeLongBlack() {
		setDes(" longblack ");
		setPrice(5.0f);
	}
}
2.5、定义装饰器: Decorator
public class Decorator extends Drink {
	
	private Drink drink ;
	
	public Decorator(Drink drink) { //组合
		this.drink = drink;
	}
	
	@Override
	public float cost() {
		// getPrice 自己价格
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// obj.getDes() 输出被装饰者的信息
		return super.getDes()+" +"+obj.getDes();
	}
}
2.6、装饰器子类,口味:Milk、Chocolate
/**
 * Decorator 的子类, 牛奶口味
 */
public class DecoratorMilk extends Decorator {

	public DecoratorMilk(Drink obj) {
		super(obj);
		setDes(" 牛奶 ");
		setPrice(2.0f); 
	}
}

/**
 * Decorator 的子类,巧克力口味
 */
public class DecoratorChocolate extends Decorator {

	public DecoratorChocolate(Drink obj) {
		super(obj);
		setDes(" 巧克力 ");
		setPrice(3.0f); // 调味品 的价格
	}
}
2.7、测试:
public class Main {

	public static void main(String[] args) {
		// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

		// 1. 点一份 LongBlack
		Drink order = new CoffeeLongBlack();
		System.out.println("费用1=" + order.cost());
		System.out.println("描述=" + order.getDes());

		
		// 2. order 加入一份牛奶
		order = new DecoratorMilk(order);

		System.out.println("加入一份牛奶 费用 =" + order.cost());
		System.out.println("加入一份牛奶 描述 = " + order.getDes());

		
		
		// 3. order 加入 巧克力口味
		order = new DecoratorChocolate(order);

		System.out.println("加入一份牛奶 加入一份巧克力  费用 =" + order.cost());
		System.out.println("加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

		
		
		// 4. order 加入 巧克力口味
		order = new DecoratorChocolate(order);

		System.out.println("加入一份牛奶 加入2份巧克力   费用 =" + order.cost());
		System.out.println("加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
	
		System.out.println("===========================");
	}
}

运行结果:

费用1=5.0
描述= longblack 
加入一份牛奶 费用 =7.0
加入一份牛奶 描述 =  牛奶  + longblack 
加入一份牛奶 加入一份巧克力  费用 =10.0
加入一份牛奶 加入一份巧克力 描述 =  巧克力  + 牛奶  + longblack 
加入一份牛奶 加入2份巧克力   费用 =13.0
加入一份牛奶 加入2份巧克力 描述 =  巧克力  + 巧克力  + 牛奶  + longblack 
===========================

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

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

相关文章

【软考】流水线

目录 一、指令控制方式1.1 说明1.2 顺序方式1.3 重叠方式1.4 流水方式 二、流水线的种类三、流水的相关处理3.1 说明3.2 RISC 中采用的流水技术3.2.1 超流水线(Super Pipe Line)技术3.2.2 超标量(Super Scalar)技术3.2.3 超长指令字(Very Long Instruction Word,VLI…

阿一网络安全之log4j2漏洞CVE-2021-44228复现

漏洞简介 Apache Log4j 2 是对 Log4j 的升级,它⽐其前身 Log4j 1.x 提供了显 着改进,并提供了 Logback 中可⽤的许多改进,同时修复了 Logback 架构中的⼀些固有问题。 2021 年 12 ⽉,在 Apache Log4j2 中发现了⼀个 0-day 漏洞。 …

倒计时7天!MoonBit 游戏挑战赛即将开启!

基于 Wasm4 框架的 MoonBit 游戏开发指南 MoonBit 即将面向全国举办“编程创新挑战赛”,并包含游戏赛道。本教程将介绍本次比赛中使用的框架 Wasm4,以及如何使用 MoonBit 在 Wasm4 框架中编写游戏。相关赛事详情见文末。 如果你曾访问过 mooncakes 或我们…

这本大模型书太香了!全方位解析LLM-Agent 第一本给程序员看的AI Agent图书!

AI Agent火爆到什么程度? OpenAI创始人奥特曼预测,未来各行各业,每一个人都可以拥有一个AI Agent;比尔盖茨在2023年层预言:AI Agent将彻底改变人机交互方式,并颠覆整个软件行业;吴恩达教授在AI…

从0-1开发一个Vue3前端系统页面-10.博客页面优化及子菜单设计

注意: 本项目已将前端源码同步上传至Gitee,项目已开源, 仅供参考,不涉及商用,不得用其牟利,著作权归本人所有。 本系列后期只会对重要部分代码进行注释,难点会同步更新至专栏 开发遇到的问题_不…

linux df -h时没有查到root盘,root文件夹带着锁或者叉号的解决办法

文章目录 一、前言二、来龙去脉1、2、给root文件赋予权限3 、这个时候df -h 查看就可以看到root文件了 总结 一、前言 当时装的双系统,自认为会学习很多linux相关课程,买了个1T的固态,ubuntu上分了很多,结果显而易见,…

UE5学习笔记16-游戏模式中的一些事件,如何改变网格体和摄像头的碰撞

一、OnPostLogIn:此事件在玩家成功登录游戏后被调用 二、HandleStartingNuwplayer:在OnPostLogIn事件后被调用,可以用来定义新进入的玩家会发生什么 三、Spawn Default PawnAtTransform:这个事件触发游戏中实际的Pawn生成 四、…

bash: /home/xxx/anaconda3/bin/conda: No such file or directory

一背景 最近把conda 移动后,出现了一堆bug,目前pip不能使用,在此记录一下解决方案。 二报错信息 bash: /home/xxx/anaconda3/envs/yolov10/bin/pip3 /home/xxx/.conda/envs/yolov10/bin/python: bad interpreter: No such file or directo…

Leetcode 100.101.110.199 二叉树相同/对称/平衡 C++实现

Leetcode 100. 相同的树 问题:给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 /*** Definition for a binary tree node.* struct T…

多媒体技术(1.1)之图像分辨率

「分辨率」这个概念还有「解析度」等说法,所以能从字面上看出来,它描述的其实就是图像包含多少细节、有多「清晰」。但具体到怎么用数字来描述一个图像有多少细节,就有很多个描述的角度,于是「分辨率」有很多种意思。 相机的分辨…

代码随想录算法训练营day29 | 贪心算法 | 134.加油站、135.分发糖果、860.柠檬水找零、406.根据身高重建队列

文章目录 134.加油站思路小结 135.分发糖果思路拓展——环形分糖小结 860.柠檬水找零思路 406.根据身高重建队列思路小结 今天是贪心算法专题第三天,直接上题目 134.加油站 建议:本题有点难度,不太好想,推荐大家熟悉一下方法二 …

STM32基础篇:定时器 × 输入捕获

通道的概念 如下图右半部分,为定时器的总体结构框图: 可以看出,在时基单元下方,有四个长条形的结构,我们将其称之为:通道1~通道4;每一个通道都会连接一个IO引脚(对应左半部分IO引脚…

OZON什么产品好卖丨OZON婴儿用具产品

Top1 摇铃 Деревянная стойка тренажер Монтессори для мобилей и игрушек для новорожденных / развивающая дуга 商品id:1557614414 月销量:707 OZON婴儿用具…

MSSQL 手工注入(第一关)

简介 SQL注入是一种安全漏洞,通过它可以执行意外的SQL命令或访问数据库中的信息。MSSQL注入通常发生在应用程序将用户输入作为SQL查询的一部分执行时,而没有对输入进行适当的验证或清理。 以下是MSSQL手工注入的流程: 一、打开靶场选择第一关…

进阶岛 多模态模型部署微调实践

一、任务介绍 follow 教学文档和视频使用QLoRA进行微调模型,复现微调效果,并能成功讲出梗图.尝试使用LoRA,或调整xtuner的config,如LoRA rank,学习率。看模型Loss会如何变化,并记录调整后效果(选做&#x…

【Electron】桌面应用开发启动直接打开一个网址或者浏览器打开一个网址

【Electron】桌面应用开发启动时直接打开一个网址或者跳转浏览器打开一个网址 前一篇有写过 Electron 桌面应用开发快速入门到打包Windows应用程序 但是现在需要程序打开的时候直接打开一个链接,在程序的窗口打开或者直接跳转浏览器打开 一、启动时直接打开一个网…

后端完成api顺序

contoroller层 Service层 点击getById,如果没有getById函数就先声明一个 然后完成函数体 db层 数据访问对象.数据库方法 //作用是提供对数据库中特定表的操作方法

【Hot100】LeetCode—437. 路径总和 III

目录 1- 思路前缀和哈希表dfs 2- 实现⭐437. 路径总和 III——题解思路 3- ACM 实现 题目连接:437. 路径总和 III 1- 思路 前缀和哈希表dfs ① 前缀和 求二叉树的前缀和,每求一次用一个 sum 传参记录更新 ② 哈希表 key 为前缀和 ,value…

k8s集群部署(sealos)

目录 部署Ubuntu22和k8s环境 环境准备 虚拟机安装ubantu 配置hosts 配置静态ip地址 配置国内阿里云的源 Master节点安装sealos软件 启用root和允许ssh远程连接 禁用firewalld和iptables 关闭交换分区 配置ipvs功能 配置时间同步 使用sealos部署k8s集群 增加K8s的ma…

[Linux#46][线程->网络] 单例模式 | 饿汉与懒汉 | 自旋锁 |读写锁 | 网络基础 | 书单

目录 1.线程安全 1.1 线程安全的单例模式 1.2 饿汉与懒汉实现方式: 实操 2.锁 3.读者写者问题 实操 4.网络基础 4.1 初始协议 书单: 1.线程安全 STL中的容器和智能指针的线程安全性整理如下:STL容器线程安全性: 状态&…