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

【设计模式】装饰器模式(Decorator)

目录

一、问题导入     

二、问题剖析

三、结构成分

四、代码实现(仅供参考)

五、优劣

1.优势

2.劣势

六、个人理解


前言:老师的课件只有意图、类图、例图和优劣,在一些细节上缺少过渡,此外,老师的课堂内容并无法准确的体现为什么要去使用装饰器模式(比如为什么我们不直接使用继承的方式或者函数去进行实现)。所以我会进行适当的扩充。(其实就是个人理解发挥比较多,可能存在不恰当的地方,希望大家能及时指出,我会尽快修改的)

一、问题导入     

在游戏《箭箭箭》中,玩家每通过一个通道,属性(如箭矢数量、攻击速度)就会发生一次改变。此时若将所有属性都封装在玩家类内部,通过调用类自身函数修改属性值,确实能实现单向的属性叠加。但这种方式存在局限 —— 如果后续要新增 “箭矢穿透”“暴击概率” 等新属性,就必须修改玩家类的源码,违背了 “对扩展开放、对修改关闭” 的设计原则。而如果用继承实现,会导致类爆炸(每增加一种属性组合就需要一个新类),且无法动态叠加 / 移除属性

而在《皇城突袭》里,场景需求更复杂。玩家不仅需要给防御塔或英雄动态叠加技能加成(如 “攻击力 + 20%”“攻击范围扩大”),还可能因误操作需要回退 —— 清除所有加成,让状态回归初始。这就要求方案同时满足两个核心诉求:一是能不修改原有系统就动态添加新能力,二是能灵活拆解已添加的能力实现回退。

恰好,装饰器模式就是为解决这类 “动态扩展 + 灵活拆解” 问题而生的经典设计方案。它通过 “对象组合” 而非 “类继承” 或 “内部修改” 的方式,既能轻松叠加新职责,又能追溯回初始状态 —— 我们接下来就具体探讨它的实现逻辑。

二、问题剖析

接下来我用一个更为简单的例子去进行解析。

在《飞机大战》中,飞机需要通过局内加成实时更改速度属性。我们可以用装饰器模式实现这一需求:

首先定义一个 Plane 基类(或接口),规定所有飞机都必须实现的核心方法(如getSpeed()获取当前速度);然后创建 BasicPlane 类,作为具体的基础飞机,实现基类的方法(比如初始速度 100)。

接下来设计装饰器抽象类 PlaneDecorator:它需要继承 Plane 基类(保证与所有飞机有相同的接口,让调用者无需区分是基础飞机还是被装饰的飞机),同时持有一个 Plane 对象(这个对象可以是基础飞机,也可以是被其他装饰器包裹过的飞机,用于保留原始状态)。

最后实现具体的装饰器(如 SpeedUpDecorator、AttackSpeedDecorator 等):每个装饰器在重写getSpeed()等方法时,会先调用被持有 Plane 对象的原始方法(比如基础飞机的 100 速度),再叠加自身的加成(比如 + 50),最终返回 150。

通过这种 “用装饰器包裹原始对象,每次叠加都基于上一层状态” 的方式,我们既能动态给飞机添加各种属性加成,又能随时移除某一层装饰器(比如去掉 SpeedUpDecorator),让飞机状态回退到上一层 —— 这正是装饰器模式的灵活之处。

三、结构成分

从问题剖析当中,我们可以抽象出其装饰器模式的组成:

(1)组件定义所有具体组件和装饰器的统一接口,保证装饰器与被装饰对象 “对外表现一致”(Plane)

(2)具体组件实现组件接口的基础功能,是装饰器的 “起点”(被装饰的原始对象)(BasicPlane)

(3)装饰器作为所有具体装饰器的基类,通过持有组件对象实现 “对原始功能的复用”,同时继承组件接口保证 “接口一致性”(PlaneDecorator)

(4)具体装饰器在装饰器基类的基础上,添加具体的额外功能(如速度加成),并在调用时先复用被装饰对象的功能(SpeedDecorator)

四、代码实现(仅供参考)

由于装饰器持有上一层对象的引用,回退时只需丢弃当前装饰器,直接使用被持有的对象即可(例如SpeedDecorator持有BasicPlane,移除时直接用BasicPlane

#pragma once
#include<iostream>namespace _DecoratorPattern
{//组件(飞机)class Plane{public:virtual int get_speed() = 0;};//具体组件class BasicPlane:public Plane{public:int get_speed() override { return 100; }};//装饰器class PlaneDecorator:public Plane{public:PlaneDecorator(Plane* plane) { this->plane = plane; }int get_speed() override { return plane->get_speed(); }private:Plane* plane;};//具体装饰器class SpeedDecorator:public PlaneDecorator{public:SpeedDecorator(Plane* plane) :PlaneDecorator(plane) {}int get_speed() override { return PlaneDecorator::get_speed() + 50; }};void test(){BasicPlane* plane = new BasicPlane();SpeedDecorator* decorator = new SpeedDecorator(plane);std::cout << decorator->get_speed() << std::endl;decorator = new SpeedDecorator(decorator);std::cout << decorator->get_speed() << std::endl;//释放内存delete plane;delete decorator;}
}

五、优劣

1.优势

(1)装饰类和被装饰类可独立开发,互不耦合

(2)可动态扩展功能

可以在运行时选择不同的装饰组合,实现灵活的功能搭配。且避免了使用继承时的类层级膨胀问题

2.劣势

存在多个装饰时会变得复杂

(调试难度增加:多层装饰器嵌套时,调用链路较长,排查问题需要逐层追溯。且必须保证装饰器与组件接口一致,否则会破坏模式的统一性)

六、个人理解

装饰器模式是在原有的基础上添加功能,像是为一个产品添加包装盒,一层一层地进行包装,想要继续包装,只需要再套上一层新的包装盒,而想要回到上一个包装,就只要拆开最外层的包装盒。而且这些包装盒(装饰器)不会改变产品(被装饰对象)本身的属性,只是在外面增加了新功能(比如包装的保护、美观作用)—— 就像装饰器不会修改组件的原有逻辑,只是在原有基础上叠加新职责。

但是所存在的问题便是如果我们想要获取中间的某一状态,或者只取出某一个中间包装,就会变得相当麻烦。就像乘法加成之后再去进行加减,就很难再去通过除法进行逆运算,想要完成这种效果,就需要进行相当复杂的数学计算了。

http://www.dtcms.com/a/520104.html

相关文章:

  • 设计模式之:享元模式
  • android 图像显示框架二——流程分析
  • CentOS 10 系统安装
  • MySQL试验部署
  • 【文献笔记】ICLR 2018 | Graph Attention Networks
  • Day69 SQLite3动态库移植 + BMP图像解析显示 + 进度条控件设计与动态文本管理
  • 通过自构建的时间服务器主机给客户端主机同步时间
  • [特殊字符] 软考架构师 vs. 考研408:全方位对比
  • C语言进阶:(一)深度剖析函数栈帧:从创建到销毁
  • 零基础从头教学Linux(Day 55)
  • 哪里有学做ppt的网站资阳的网站建设
  • Apple 开源FastVLM:AI看图说话更快更准
  • 交互式UTM坐标查询工具:让地理坐标转换变得简单
  • 初学者小白复盘15之指针(4)
  • 轻量级且简单的 macOS 应用 Forklift for mac
  • 和平板电脑厂商定制智慧养老平板有那种合作模式?
  • 无人机安防体系的音视频超低延迟重构:从“空地融合”到“实时智控”
  • 做网站推广业务怎么样专业仿站网站建设
  • 三分钟部署最新开源大模型!Amazon SageMaker JumpStart 生成式 AI 实战指南
  • AWS云服务故障复盘——从故障中汲取的 IT 运维经验
  • Adobe Dimension 2025 (3D可视化设计神器) 解锁版
  • CUDA安装备忘录
  • 泰安网站建设流程软文营销文章300字
  • 医院为什么要做门户网站建设无锡专业网站推广
  • freeRTOS学习
  • K8s 集群环境搭建 - yaml 版本(一)
  • RAM和ROM的定义和区别总结!!!
  • GELU(高斯误差线性单元)激活函数全面解析
  • 企业网站可以做淘宝客吗wordpress 用户密码加密
  • WordPress + React 无头架构搭建指南