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

【设计模式】装饰模式

六、装饰模式

装饰(Decorator) 模式也称为装饰器模式/包装模式,是一种结构型模式。这是一个非常有趣和值得学习的设计模式,该模式展现出了运行时的一种扩展能力,以及比继承更强大和灵活的设计视角和设计能力,甚至在有些场合下,不使用该模式很难解决问题。
在本模式的讲解过程中,会提及类与类之间的继承关系和组合关系,还会引出面向对象程序设计的一个重要原则——组合复用原则。

6.1 问题的提出

继续前面的闯关打斗类游戏。在游戏中,不但会出现各种人物、怪物,还会出现许多UI (用户接口)界面。例如,主角一般会随身携带背包,背包中的每个格子用于放置一个物品,根据策划的规定,背包中格子数量比较多时,还会在背包右侧显示出滚动条,如图6.1所示。

在这里插入图片描述

再例如,有时需要显示一些公告信息,如图6.2所示。当公告信息太长时,也可能会出现滚动条等。
图6.1和图6.2这些看得见的UI界面元素称为控件。例如,要向其中输入文字可以使用文本控件,要显示多行信息可以使用列表控件等,当要显示的内容过长或者过宽时会显示出滚动条控件。

在这里插入图片描述

这里就以一个最简单的控件——列表控件为例,说明如何丰富该控件上所显示的内容,图6.3中第1幅子图是一个普通的列表控件,第2幅图增加了边框让其更有立体感,第3幅图增加了一个垂直滚动条,而第4幅图又增加了一个水平滚动条。

在这里插入图片描述

传统继承方案的问题

  1. 子类膨胀:新增功能(如阴影、外发光)需不断创建子类。
  2. 灵活性差:无法灵活组合功能(如无框但有垂直滚动条的控件)。

解决方案思路

采用组装方式动态添加功能:

  1. 基础控件(ListCtrl)作为核心。
  2. 附加功能(边框、滚动条)作为装饰器,通过组合方式叠加。

6.2 引入装饰模式

组合复用原则(CRP)

核心思想:优先使用组合而非继承,以降低类间耦合,避免父类代码冗余。

装饰模式实现代码

1. 抽象构件(Control)
// 抽象的控件类
class Control {
public:
    virtual void draw() = 0;
public:
    virtual ~Control() {}
};
2. 具体构件(ListCtrl)
// 列表控件类
class ListCtrl : public Control {
public:
    virtual void draw() {
        cout << "  绘制普通的列表控件!" << endl;
    }
};
3. 抽象装饰器(Decorator)
// 抽象的装饰器类
class Decorator : public Control {
public:
    Decorator(Control* tmpctrl) : m_control(tmpctrl) {}  // 组合关系
    virtual void draw() {
        m_control->draw();  // 委托给被装饰对象
    }
private:
    Control* m_control;
};
4. 具体装饰器

边框装饰器

class BorderDec : public Decorator {
public:
    BorderDec(Control* tmpctrl) : Decorator(tmpctrl) {}
    virtual void draw() {
        Decorator::draw();  // 先绘制原内容
        drawBorder();       // 再绘制新增内容
    }
private:
    void drawBorder() { cout << "  绘制边框!" << endl; }
};

垂直滚动条装饰器

class VerScrollBarDec : public Decorator {
public:
    VerScrollBarDec(Control* tmpctrl) : Decorator(tmpctrl) {}
    virtual void draw() {
        Decorator::draw();
        drawVerScrollBar();
    }
private:
    void drawVerScrollBar() { cout << "  绘制垂直滚动条!" << endl; }
};

客户端代码示例

int main() {
    // 组装带边框和垂直滚动条的控件
    Control* base = new ListCtrl();
    Control* withBorder = new BorderDec(base);
    Control* final = new VerScrollBarDec(withBorder);
    final->draw();  // 输出:普通列表 → 边框 → 垂直滚动条

    cout << "------------------------" << endl;

    // 组装只带水平滚动条的控件
    Control* base2 = new ListCtrl();
    Control* withHor = new HorScrollBarDec(base2);
    withHor->draw();  // 输出:普通列表 → 水平滚动条

    // 释放资源(注意顺序)
    delete final;
    delete withBorder;
    delete base;
    delete withHor;
    delete base2;
    return 0;
}

装饰模式UML图

m_control
«abstract»
Control
+draw()
ListCtrl
+draw()
«abstract»
Decorator
-m_control: Control
+draw()
BorderDec
+draw()
-drawBorder()
VerScrollBarDec
+draw()
-drawVerScrollBar()
HorScrollBarDec
+draw()
-drawHorScrollBar()

模式角色

角色说明示例类
抽象构件定义统一接口Control
具体构件基础功能实现ListCtrl
抽象装饰器持有构件引用,定义装饰接口Decorator
具体装饰器实现具体装饰逻辑BorderDec

6.3 饮料价格计算范例

问题描述

  • 基础饮料:10元
  • 可选配料:砂糖(+1元)、牛奶(+2元)、珍珠(+2元)

实现代码

1. 抽象构件(Beverage)
class Beverage {
public:
    virtual int getPrice() = 0;
    virtual ~Beverage() {}
};
2. 具体构件(FruitBeverage)
class FruitBeverage : public Beverage {
public:
    int getPrice() override { return 10; }
};
3. 抽象装饰器(CondimentDecorator)
class CondimentDecorator : public Beverage {
protected:
    Beverage* beverage;
public:
    CondimentDecorator(Beverage* b) : beverage(b) {}
};
4. 具体装饰器

砂糖装饰器

class Sugar : public CondimentDecorator {
public:
    Sugar(Beverage* b) : CondimentDecorator(b) {}
    int getPrice() override { return beverage->getPrice() + 1; }
};

珍珠装饰器

class Bubble : public CondimentDecorator {
public:
    Bubble(Beverage* b) : CondimentDecorator(b) {}
    int getPrice() override { return beverage->getPrice() + 2; }
};

客户端代码

int main() {
    Beverage* drink = new FruitBeverage();
    drink = new Bubble(drink);  // 加珍珠(+2)
    drink = new Sugar(drink);   // 加砂糖(+1)
    
    cout << "总价格:" << drink->getPrice() << "元" << endl;  // 输出:13元
    delete drink;
    return 0;
}

饮料范例UML图

beverage
«abstract»
Beverage
+getPrice()
FruitBeverage
+getPrice()
«abstract»
CondimentDecorator
-beverage: Beverage
+getPrice()
Sugar
+getPrice()
Bubble
+getPrice()

总结:装饰模式通过组合实现动态功能扩展,避免继承导致的类爆炸,符合开闭原则,适用于需要灵活添加可选功能的场景。

相关文章:

  • Apache Tomcat CVE-2025-24813 安全漏洞
  • AI视频是否会影响原创价值
  • 如何提升库存系统的高并发和稳定性:算法与设计模式
  • 6.5840 Lab 3: Raft
  • 使用 Docker 构建 LangChain 开发环镜及 ChatOllama 示例
  • vscode/cursor中python运行路径设置 模块导入问题
  • vscode git 管理
  • PostgreSQL_数据表结构设计并创建
  • Unity将运行时Mesh导出为fbx
  • Nordic nRF 蓝牙的 Direct Test Mode (DTM) 测试介绍
  • 强大的AI网站推荐(第二集)—— V0.dev
  • 第六篇:Setup:组件渲染前的初始化过程是怎样的?
  • 【网络安全】从浏览器到服务端讲JavaScript安全
  • 星型拓扑网络发生网络风暴
  • 鸿蒙ArkTS+ArkUI实现五子棋游戏
  • C++ 异常 【无敌详细版】
  • 用逻辑分析仪分析Usart波形
  • 如何使用ncurses库并在Linux上编译?
  • C++Primer学习(13.1 拷贝、赋值与销毁)
  • OSPF-8 OSPF特殊区域NSSA
  • 卢正已任上海市司法局党委委员、副局长
  • 黑灰产工作室为境外诈骗集团养号引流,冒充美女与男性裸聊后敲诈勒索
  • 名帅大挪移提前开启,意属皇马的阿隆索会是齐达内第二吗
  • 长江画派创始人之一、美术家鲁慕迅逝世,享年98岁
  • 全国人大常委会启动食品安全法执法检查
  • 暴利之下:宠物殡葬行业的冰与火之歌