当前位置:首页 > 技术 > 正文内容

装饰者模式:动态扩展对象功能的灵活方案

访客 技术 2026年8月9日 2

核心思想

装饰者模式允许在不修改原有类结构的前提下,动态地为对象添加新的行为或职责。相比通过继承创建子类来扩展功能,该模式提供了更高的灵活性和可维护性。

关键角色说明

  • Component(组件接口):定义被装饰对象的公共接口,所有具体组件和装饰器都实现此接口。
  • ConcreteComponent(具体组件):实现组件接口的具体类,代表原始功能的承载者。
  • Decorator(装饰基类):抽象类,继承自Component,持有对Component实例的引用,并提供统一的装饰逻辑框架。
  • ConcreteDecorator(具体装饰器):继承Decorator类,实现具体的增强逻辑,可在调用原方法前后插入额外行为。

典型实现代码(C#)

abstract class Component
{
    public abstract void Execute();
}

class BasicService : Component
{
    public override void Execute()
    {
        Console.WriteLine("执行基础服务");
    }
}

abstract class ServiceWrapper : Component
{
    protected Component innerService;

    public void SetInnerService(Component service)
    {
        innerService = service;
    }

    public override void Execute()
    {
        if (innerService != null)
            innerService.Execute();
    }
}

class LoggingDecorator : ServiceWrapper
{
    public override void Execute()
    {
        Console.WriteLine("[日志] 开始处理请求");
        base.Execute();
        Console.WriteLine("[日志] 请求处理完成");
    }
}

class CachingDecorator : ServiceWrapper
{
    public override void Execute()
    {
        Console.WriteLine("[缓存] 检查是否已有结果");
        base.Execute();
        Console.WriteLine("[缓存] 结果已保存");
    }
}

客户端使用示例

static void Main(string[] args)
{
    var basic = new BasicService();
    var logging = new LoggingDecorator();
    var caching = new CachingDecorator();

    logging.SetInnerService(basic);
    caching.SetInnerService(logging);

    caching.Execute();
}
执行流程如下: 1. 输出"[缓存] 检查是否已有结果" 2. 输出"[日志] 开始处理请求" 3. 输出"执行基础服务" 4. 输出"[日志] 请求处理完成" 5. 输出"[缓存] 结果已保存"

适用场景

  • 需要在运行时动态地为对象添加功能,且不想影响其他实例。
  • 功能的组合方式较多,若使用继承会导致类爆炸。
  • 希望保持原始类的简洁性,避免将多种职责耦合在一起。

优势与注意事项

  • 无需修改已有代码即可扩展功能。
  • 支持递归嵌套装饰,实现复杂的功能链。
  • 每个装饰器职责单一,易于测试和复用。
  • 过度使用可能导致对象结构复杂,难以追踪执行顺序。

C++版本示例

#include <iostream>
using namespace std;

class Device {
public:
    virtual ~Device() = default;
    virtual void Display() = 0;
};

class SmartPhone : public Device {
private:
    string model;
public:
    SmartPhone(const string& name) : model(name) {}
    void Display() override {
        cout << "显示手机:" << model << endl;
    }
};

class Decorator : public Device {
protected:
    Device* wrappedDevice;
public:
    Decorator(Device* device) : wrappedDevice(device) {}
    void Display() override {
        if (wrappedDevice) wrappedDevice->Display();
    }
};

class ScreenProtector : public Decorator {
public:
    ScreenProtector(Device* device) : Decorator(device) {}
    void Display() override {
        Decorator::Display();
        cout << "屏幕已贴膜" << endl;
    }
};

class CaseDecorator : public Decorator {
public:
    CaseDecorator(Device* device) : Decorator(device) {}
    void Display() override {
        Decorator::Display();
        cout << "已安装保护壳" << endl;
    }
};

int main() {
    Device* phone = new SmartPhone("Xiaomi 14");
    Device* withCase = new CaseDecorator(phone);
    Device* withCaseAndScreen = new ScreenProtector(withCase);

    withCaseAndScreen->Display();

    delete withCaseAndScreen;
    delete withCase;
    delete phone;
    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。