• 我的订阅
  • 科技

装饰器模式的基础

类别:科技 发布时间:2023-01-12 21:22:00 来源:浅语科技

大家好,我是每周在这里陪你进步的网管~,本次我们继续填坑,说一下装饰器模式。

上篇文章我们说过装饰器是代理模式的特殊应用,而且很多人说中间件是用装饰器模式实现的,有的人说是用职责链实现的,那么这篇文章我们就来一起看看他们的异同。什么是装饰器

装饰器模式(DecoratorPattern)也叫作包装器模式(WrapperPattern),指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构型设计模式。

给对象添加新行为最简单直观的办法就是扩展本体对象,通过继承的方式达到目的。但是使用继承不可避免地有如下两个弊端:

继承是静态的,在编译期间就已经确定,无法在运行时改变对象的行为。

子类只能有一个父类,当需要添加的新功能太多时,容易导致类的数量剧增。

而使用装饰器模式,我们通过将现有对象放置在实现了相同一套接口的包装器对象中来动态地向现有对象添加新行为。在包装器中进行我们代码的扩展,有助于重用功能并且不会修改现有对象的代码,符合“开闭原则”。

这里被放置在包装对象的“现有对象”通常会被叫做“组件”(Component),而包装组件的包装器对象就是我们常说的“装饰器”(Decorator),因为装饰器会组件实现相同接口,故客户端无法识别两者的差异,也就不需要在增加装饰器时对客户端调用代码进行修改了。

从上面关于装饰器模式的描述中,会感觉他跟代理模式很像。这是因为他们本来在结构上也几乎一样,装饰器算是代理的一个特殊应用--装饰器模式的一个特点是可以嵌套多层装饰器,相当于给代理再加代理。不过代理强调的是对本体对象的访问控制,而装饰器是用来对本地进行增强,两者在使用目的上不一样。

上面装饰器模式的用处特点用文字描述了这么多,下面我们用UML类图展示一下它的结构,让我们在写代码前对模式中的各个角色有个更清晰的认识。装饰器的结构

用UML类图表示装饰器模式的结构如下:

装饰器模式的基础

从图中可以看到装饰器模式中主要有如下几个角色:

客户端:会用多层装饰器来封装组件,最后调用装饰好的包装器的方法,启动执行。

组件接口:Component声明装饰器对象和被装饰的组件对象要实现的公用接口。

组件实现:具体的组件实现类它的Operation方法中定义了组件的基础行为,装饰类可以增强这些行为。

基础装饰类:拥有一个指向被封装对象的成员变量。在自己的Operation方法中调用被装饰对象的Operation方法

具体装饰类:重写父类的Operation方法实现增强逻辑。类图里已经给出了要实现的主要逻辑,第四步的基础装饰类并不需要一定存在,完全可以由具体装饰类来持有对被装饰对象的引用,并实现增强逻辑,这样一来整体的结构会更简单一些。

注意:图中的方法名在代码实现里可自己定义,不需要完全跟图里给出的方法名一样。

我们可以跟上节代理模式的UML类图做个对比,两者在结构上非常相似,尤其是省略了BaseDecorator这一层后,在结构上基本上是一摸一样,这样我们一直再强调的--"装饰器是代理模式的特殊应用"的一个论据。

下面我们看一下实现装饰器模式的代码模版,本文中提供了Go语言实现一个简单装饰器模式的代码模版。装饰器模式代码实现

清楚了装饰器模式结构的组成后,再来写代码就会清晰很多,接下来我们演示一下用装饰器模式实现增强游戏主机的一个例子。

首先我们定义一个游戏主机的产品接口,它就是上面类图中组件和装饰器的公共接口。// PS5 产品接口type PS5 interface { StartGPUEngine() GetPrice() int64}

然后我们提供一个基础的产品实现类作为装饰器模式中的组件。// CD 版 PS5主机"本文使用的完整可运行源码去公众号「网管叨bi叨」发送【设计模式】即可领取"type PS5WithCD struct{}func (p PS5WithCD) StartGPUEngine() { fmt.Println("start engine")}func (p PS5WithCD) GetPrice() int64 { return 5000}

这里给出的是一个CD版的游戏主机,平时玩游戏的同学都会知道,一般还会有数字版的主机,价格会便宜点,这种情况我们可以提供一个数字版游戏主机的实现作为组件实现类。// PS5 数字版主机type PS5WithDigital struct{}func (p PS5WithDigital) StartGPUEngine() { fmt.Println("start normal gpu engine")}func (p PS5WithDigital) GetPrice() int64 { return 3600}

那么除了这两种基础的产品类型,厂商一般还会开发各种主题限定配色的主机、增加了硬件配置的主机等等,这两种在价格上肯定会跟基础版有些不一样,针对这种层面的扩展我们可以使用装饰器来实现,避免对基础组件类的更改。

下面是用两个装饰器实现的Plus版和主题配色版的两个增强。"本文使用的完整可运行源码去公众号「网管叨bi叨」发送【设计模式】即可领取"// Plus 版的装饰器func (p *PS5MachinePlus) SetPS5Machine(ps5 PS5) { p.ps5Machine = ps5}func (p PS5MachinePlus) StartGPUEngine() { p.ps5Machine.StartGPUEngine() fmt.Println("start plus plugin")}func (p PS5MachinePlus) GetPrice() int64 { return p.ps5Machine.GetPrice() + 500}// 主题色版的装饰器type PS5WithTopicColor struct { ps5Machine PS5}func (p *PS5WithTopicColor) SetPS5Machine(ps5 PS5) { p.ps5Machine = ps5}func (p PS5WithTopicColor) StartGPUEngine() { p.ps5Machine.StartGPUEngine() fmt.Println("尊贵的主题色主机,GPU启动")}func (p PS5WithTopicColor) GetPrice() int64 { return p.ps5Machine.GetPrice() + 200}

根据装饰器模式的特点,两个增强还可以叠加在一起,组合出即高配主题限定版主机......呃,是不是有点某游戏大厂每年发新机时给你的感觉了,就是不出第二代,每年给你多发几个限定配色、升级下屏幕,说的就是你XXX(各位自己评论里脑补一下)

好了,在客户端我们把装饰器和组件组合起来就能获得一款高配主题限定版主机......"本文使用的完整可运行源码去公众号「网管叨bi叨」发送【设计模式】即可领取"func main() { ps5MachinePlus := PS5MachinePlus{} ps5MachinePlus.SetPS5Machine(PS5WithCD{}) // ps5MachinePlus.SetPS5Machine(PS5WithDigital{}) // 可以在更换主机 ps5MachinePlus.StartGPUEngine() price := ps5MachinePlus.GetPrice() fmt.Printf("PS5 CD 豪华Plus版,价格 %d 元\n\n", price ps5WithTopicColor := PS5WithTopicColor{} ps5WithTopicColor.SetPS5Machine(ps5MachinePlus) ps5WithTopicColor.StartGPUEngine() price = ps5WithTopicColor.GetPrice() fmt.Printf("PS5 CD 豪华Plus 经典主题配色版,价格 %d 元\n", price}

装饰器模式的基础

装饰器和几个模式的区别

装饰器和代理在结构上类似,在行为上跟职责链模式类似,现在我们总结一下他们之间的区别装饰器模式VS代理模式

装饰器模式就是代理模式的一个特殊应用。

装饰器模式强调自身功能的扩展。

代理模式强调对代理过程的控制。装饰器VS职责链模式

装饰器和职责链在行为上看都是多个单元进行组合完成逻辑处理,但是装饰器注重给某样东西添加扩展,最终会得到一个产品。而职责链更强调分步骤完成某个流程,更像是一个任务链表,而且与装饰器模式不同的是,职责链可以随时终止。

举个例子来说,针对OA系统请假审批这个场景,假设员工请假需要得到组长、总监和经理的批准才行。在这种情况下,使用装饰器模式实现的话无论您的请假在前面的环节被批准还是被拒绝,整个链条都不会中断,最终我们会得到三个级别审批人对申请的全部反馈。

而使用职责链模式的话,在每个阶段,每个审批人都有权批准或拒绝。如果请求在任何级别被拒绝,那么整个流程就会结束,请求不会继续流转到下一个级别的审批人那里。

所以看到这里,你觉得像Web框架的中间件这种东西应该拿职责链还是装饰器实现呢?总结

装饰器模式有不少优点,它是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果,完全遵循程序设计的“开闭原则”。

但装饰器的使用必将会给程序带来更高的复杂性,更低的可读性,子类集成的代码结构会更直白易懂一些,而且虽然装饰器符合“开闭原则”,但是它会给程序带来更多的类,动态装饰在多层装饰时会更复杂。

所以总体上使用装饰器模式的时候也是两害相较取其轻,为了不频繁修改已经成型的子类而引入更多装饰器类。

应用的时候一定要谨记装饰器是“增强”某个事物用的,可千万别把事物本身实现的主逻辑用装饰器实现了。

以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。

快照生成时间:2023-01-13 06:45:10

本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。

信息原文地址:

6大功能特性升级,UCloudStack优刻得私有云发布全新版本
...及 PaaS 等全栈云服务,技术架构轻量灵活、支持多种交付模式高效部署。近日,UCloudStack优刻得私有云正式发布V2
2023-06-20 15:00:00
...作“探索太空的先驱”,是一种有上百年研究历史的重要模式生物,曾被多次应用于太空实验。果蝇只有4对染色体,便于进行遗传学实验,它的许多基因与人类基因具有相似性。利用果蝇这个有力
2024-12-08 13:49:00
ORM 是“反模式” 吗?
...焦点。你可以在网络上发现大量文章唱反调:“ORM 是反模式,它们只是创业公司的小玩具,整体来说弊大于利。” 这其实有些过分夸大其词。ORM 并非全无用处。正如软件领域里的技术
2023-07-05 21:00:00
任天堂:将于今年晚些时候公布下一代switch主机
...够向下兼容现有的Switch游戏。同时,新主机还将支持掌机模式和通过底座连接后最高4K分辨率的电视模式。需要注意的是,目前为止所有关于新主机的消息均未得到任天堂官方确认,也未
2024-05-09 02:21:00
微软Win11Release预览版 发布
...的问题。当将系统区域设置为日语并且cmd.exe配置为传统模式时,会导致失败。解决了影响设置中记事本框的问题。无法显示所有可用选项
2023-03-17 20:12:00
什么是空气能热泵三联供?
...“生活热水”、“制热”、“制热兼生活热水”五种工作模式,用户可根据实际需求设定不同的工作模式。在同时需要空调和生活热水的情况下,设定“制冷兼生活热水模式”。在此模式下,当空调
2023-01-07 03:00:00
微软考虑将更多游戏主机功能带到PC上,或整合快速恢复功能
...能不得不中止游戏才行。反观游戏主机,即便进入低功耗模式也不会丢失游戏的进度。近日,微软Xbox软件主管RoanneSoanes在讨论华硕ROGAlly掌机的一些功能时
2023-05-19 00:04:00
案例│青藤MSS安全托管服务助力客户安全无忧
...事件闭环。实战效果不同于“设备到人即走”的常规交付模式,青藤全年远程顾问式服务直击用户心坎,专业安全团队协助用户完成事件的响应和处置,获得用户好评。主机漏洞有专业团队分级评估
2023-08-10 11:00:00
星帅尔:2023年净利润同比增长70%,将重点向光伏领域进军
...两翼“以家电板块为基础、以新能源板块为先导”的发展模式。新能源业务突飞猛进星帅尔是家电制冷压缩机起动器、热保护器细分领域的龙头企业,2017年于深交所主板上市。公司以冰箱、冷
2024-04-10 09:25:00
更多关于科技的资讯: