结构型模式 - 装饰者模式 (Decorator Pattern)

结构型模式 - 装饰者模式 (Decorator Pattern)

在展开讲装饰者模式之前,不得不提一下代理模式,因为这两者在一定的层度上是有相似性的, 通过对比可以让我们更好的理解装饰者.

定义与核心目的

  • 装饰者模式
    • 定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
    • 核心目的:主要是为对象添加额外的功能,强调的是对对象功能的增强和扩展,是在不改变原有对象结构的基础上,给对象增加新的行为。
  • 代理模式
    • 定义:为其他对象提供一种代理以控制对这个对象的访问。
    • 核心目的:主要是控制对对象的访问,代理对象充当了客户端和目标对象之间的中介,客户端通过代理对象来间接访问目标对象,从而可以在访问前后进行一些额外的操作,如权限验证、缓存等。

接下来通过代码对比一下装饰者模式、代理模式

// 装饰者模式// 定义一个接口
interface Component {void operation();
}// 具体组件
class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("执行基本操作");}
}// 装饰者抽象类
abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}// 具体装饰者
class ConcreteDecorator extends Decorator {public ConcreteDecorator(Component component) {super(component);}@Overridepublic void operation() {super.operation();System.out.println("添加额外操作");}
}// 测试代码
public class DecoratorPatternExample {public static void main(String[] args) {Component component = new ConcreteComponent();Component decoratedComponent = new ConcreteDecorator(component);decoratedComponent.operation();}
}

如果把上面装饰者的例子中 Decorator 类去掉抽象 abstract 修饰, 然后把持有的 Component 改成具体对象 ConcreteComponent 并在构造方法里面直接创建要代理的对象, 那它就变成静态代理模式!!!

下面这是一个代理模式例子, 来看看它俩的相似之处吧.

// 定义一个接口
interface Subject {void request();
}// 真实主题
class RealSubject implements Subject {@Overridepublic void request() {System.out.println("执行真实请求");}
}// 代理主题
class ProxySubject implements Subject {private RealSubject realSubject;public ProxySubject() {this.realSubject = new RealSubject();}@Overridepublic void request() {System.out.println("在请求前进行一些操作");realSubject.request();System.out.println("在请求后进行一些操作");}
}// 测试代码
public class ProxyPatternExample {public static void main(String[] args) {ProxySubject proxy = new ProxySubject();proxy.request();}
}

再看一个经典的装饰者模式, 来加强一下装饰者的理解

// 抽象组件:饮品
interface Beverage {String getDescription();double cost();
}// 具体组件:浓缩咖啡
class Espresso implements Beverage {@Overridepublic String getDescription() {return "浓缩咖啡";}@Overridepublic double cost() {return 2.0;}
}// 抽象装饰者:调料
abstract class CondimentDecorator implements Beverage {protected Beverage beverage;public CondimentDecorator(Beverage beverage) {this.beverage = beverage;}
}// 具体装饰者:牛奶
class Milk extends CondimentDecorator {public Milk(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ",加牛奶";}@Overridepublic double cost() {return beverage.cost() + 0.5;}
}// 具体装饰者:巧克力
class Chocolate extends CondimentDecorator {public Chocolate(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ",加巧克力";}@Overridepublic double cost() {return beverage.cost() + 1.0;}
}// 测试类
public class CoffeeShopExample {public static void main(String[] args) {// 创建一个浓缩咖啡Beverage espresso = new Espresso();System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");// 给浓缩咖啡加牛奶Beverage espressoWithMilk = new Milk(espresso);System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");// 给加了牛奶的浓缩咖啡再加巧克力Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");}
}

这时候我们可以再创建一个装饰者, 比如配送方式的装饰者,看顾客是到店自取还是外卖配送, 因为这两种方式价格是不一样的.
这时候我们只需要另外建一个具体装饰者类即可.

// 抽象配送装饰者
abstract class DeliveryDecorator implements Beverage {protected Beverage beverage;public DeliveryDecorator(Beverage beverage) {this.beverage = beverage;}
}// 具体配送装饰者:外卖配送
class DeliveryByCourier extends DeliveryDecorator {public DeliveryByCourier(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ",外卖配送";}@Overridepublic double cost() {return beverage.cost() + 3.0; // 假设外卖配送费 3 元}
}// 具体配送装饰者:到店自取
class PickupInStore extends DeliveryDecorator {public PickupInStore(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ",到店自取";}@Overridepublic double cost() {return beverage.cost(); // 到店自取无额外费用}
}// 测试类
public class CoffeeShopExample {public static void main(String[] args) {// // 创建一个浓缩咖啡// Beverage espresso = new Espresso();// System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");// // 给浓缩咖啡加牛奶// Beverage espressoWithMilk = new Milk(espresso);// System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");// // 给加了牛奶的浓缩咖啡再加巧克力// Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);// System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");// 使用的时候就可以 2 选 1 种配送// 选择外卖配送Beverage espressoWithAllAndDelivery = new DeliveryByCourier(espressoWithMilkAndChocolate);System.out.println(espressoWithAllAndDelivery.getDescription() + ",价格:" + espressoWithAllAndDelivery.cost() + " 元");// 选择到店自取Beverage espressoWithAllAndPickup = new PickupInStore(espressoWithMilkAndChocolate);System.out.println(espressoWithAllAndPickup.getDescription() + ",价格:" + espressoWithAllAndPickup.cost() + " 元");}
}

这时候假设我们需求又双叒变更了, 我们只需要增加装饰者, 比如说要按顾客的会员等级对订单总额打折处理… 发挥你的想象吧, 它有无限的可能~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python系列】PYTHONUNBUFFERED=1的作用

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Adobe After Effects的动画制作

作者:余佳琪 目录 一、 前言 二、 可动骨骼的选择 三、 运动曲线的设置 四、 图层的选定与应用 五、 插件的应用(阴影,高光,特效) 六、 导出 一、 前言 在当今世界&#x…

可狱可囚的爬虫系列课程 14:10 秒钟编写一个 requests 爬虫

一、前言 当重复性的工作频繁发生时,各种奇奇怪怪提高效率的想法就开始萌芽了。当重复代码的模块化封装已经不能满足要求的时候,更高效的方式就被揭开了神秘的面纱。本文基于这样的想法,来和大家探讨如何 10 秒钟编写一个 requests 爬虫程序。…

QNX上如何抓tracelogger日志

背景 因QNX侧 QVM的分析CPU负载问题在android侧使用trace无法分析,故QNX侧的CPU负载问题需要用到tracelogger日志分析。 例如:使用hogs -l 42|grep qvm 中发现qvm的cpu负载 30%多 但是使用trace日志在Perfetto又查不到qvm信息,则需要抓取qn…

DeepSeek开源周 Day02:从DeepEP开源趋势重新审视大模型Infra

DeepEP 今天DeepSeek开源周第二天,开放了DeepEP仓库,属实看了下源码,和昨天FlashMLA一样,C权重(包括CUDA)还是占据了绝对部分,作为调包侠的我,看到之后望而却步,想看原理…

【Ambari】Ranger KMS

目录 一、Ranger KMS介绍 二、KMS基于Ranger插件安装 一、Ranger KMS介绍 Ranger KMS是把数据存储入后台数据库中。通过Ranger Admin可以集中化管理KMS服务。 Ranger KMS有三个优点 l Key management Ranger admin 提供了创建,更新,删除密钥的Web UI…

055 SpringCache

文章目录 缓存一致性Spring Cachepom.xmlapplication.ymlCubemallProductApplication.javaSpringCache改造三级分类MyCacheConfig.java缓存一致性 缓存一致性 锁 设置过期时间 读写锁 设置过期时间 Spring Cache 1.读模式 缓存穿透:查询一个null数据,…

神卓 S500 组网设备连接交换机的详细步骤

神卓 S500 组网设备连接交换机的详细步骤 神卓 S500 组网设备以其高效、灵活的解决方案,在异地监控组网中发挥着重要作用。本文将详细介绍神卓 S500 组网设备连接交换机的步骤,帮助您轻松实现网络的互联互通。 一、前期准备 确认设备型号与规格&#x…

图像处理案例06 OCR应用

OCR应用 1 OCR读取账单1.1 背景及思路1.2 代码 1 OCR读取账单 1.1 背景及思路 思路 目标是读取图片中账单的信息。首先要截取图片上的账单,考虑到账单并非都是整齐摆放,为了保持算法的通用性,通过透视变换对扣取的账单摆正,然后调…

2024最新版鸿蒙纯血原生应用开发教程文档丨学习ArkTS语言-基本语法

ArkTS是HarmonyOS的主要应用开发语言,在TypeScript基础上进行了扩展,保留了其基本风格,并通过增强静态检查和分析来提高程序的稳定性和性能。本教程将帮助开发者掌握ArkTS的核心功能、语法及最佳实践,以便高效地构建高性能移动应用…

【AIGC】使用Python实现科大讯飞语音服务ASR转录功能:完整指南

文章目录 讯飞ASR转写API完整指南1. 引言2. 讯飞ASR API介绍3. API参数说明3.1 认证参数3.2 上传参数3.3 查询结果参数3.4 orderResult 字段3.5 Lattice 字段3.6 json_1best 字段3.7 st 字段 4. Python代码实现4.1 生成签名4.2 上传音频文件4.3 获取转写结果4.4 解析转写结果 5…

微软开源神器OmniParser-v2.0本地部署教程

安装python环境 我这里是以前安装好的版本:python 3.11.5,这里不再介绍,有需要的可以在网上找教程。 安装Anaconda 我这里是以前安装好的版本:conda 23.7.4,这里也不再介绍,有需要的可以在网上找教程。 …

LLM+多智能体协作:基于CrewAI与DeepSeek的邮件自动化实践

文章目录 引言理解 Flows(工作流)与 Crews(协作组)一、环境准备与工具安装1.1 Python环境搭建1.2 创建并激活虚拟环境1.3 安装核心依赖库(crewai、litellm) 二、本地DeepSeek R1大模型部署2.1 Ollama框架安…

Linux——高级IO

一、前言概念 IO拷贝等待 1. 同步(Synchronous) vs 异步(Asynchronous) 核心区别:关注的是消息通知的机制。 同步:调用方主动等待结果,需持续检查任务是否完成。 异步:调用方发起…

Linux:基础IO

文章目录 一、理解"文件"1、狭义上的理解2、广义上的理解3、文件操作的认知4、系统角度 二、C语言文件接口1、ls /proc/[进程id] -l 命令查看当前正在运⾏进程的信息2、stdin 和 stdout 和 stderr 三、系统文件 I/O1、标志位传递的一种方法2、系统调用 open 三、文件…

zabbix数据采集以及自定义监控

目录 1.数据采集 2.自定义监控 2.1客户端 2.2服务端 ​​​​​​​1.数据采集 点击最新数据页面如下图 往下滑查看具体数据 并点击查看图像就可以看到图像了 就可以看到如下图内容 2.自定义监控 我们通过zabbix客户端任何模板就可以监控我们想要的任何资源 如果…

02.25 继承和多态

编写一个如下场景: 有一个英雄Hero类,私有成员,攻击,防御,速度,生命值,以及所有的set get 方法 编写一个 武器 Weapon 类,拥有私有成员攻击力,以及set get 方法 编写一个…

6. grafana的graph简介

1. Settings功能 2. Visualization功能 (可视化的方式,后续会写一些) 3. Display 功能(显示方面的设置) bars 柱状图方式显示 lines(不选不会出功能) line width 线条的粗细 staircase 会让折…

前缀和代码解析

前缀和是指数组一定范围的数的总和,常见的有两种,一维和二维,我会用两道题来分别解析 一维 DP34 【模板】前缀和 题目: 题目解析: 暴力解法 直接遍历数组,遍历到下标为 l 时,开始进行相加,直到遍历到下标为 r ,最后返回总和.这样做的时间复杂度为: O(n) public class Main …

RoCBert:具有多模态对比预训练的健壮中文BERT

摘要 大规模预训练语言模型在自然语言处理(NLP)任务上取得了最新的最优结果(SOTA)。然而,这些模型容易受到对抗攻击的影响,尤其是对于表意文字语言(如中文)。 在本研究中&#xff0…