【设计模式】深入解析装饰器模式(Decorator Pattern)

深入解析装饰器模式(Decorator Pattern)

一、装饰器模式的核心概念

装饰器模式是一种结构型设计模式 ,用于动态地给对象添加新功能 ,而不改变其原始代码

1. 为什么需要装饰器?

  • 避免继承带来的类爆炸问题:如果每种新功能都创建子类,组合复杂时类会爆炸式增长。
  • 支持动态扩展功能 :继承是静态 的,而装饰器可以在运行时动态添加/移除功能
  • 遵循开闭原则(OCP) :不修改原始代码,而是通过装饰器增强。

二、装饰器的核心机制

装饰器本质上就是一个高阶函数 ,它接收一个函数(或类)作为参数,返回一个新的函数(或类) ,增强原有功能。

装饰器的执行顺序

  1. 定义装饰器

    • 编写一个函数 ,接收原函数 func,在 wrapper 中增加新功能。
  2. 应用装饰器

    • 通过 @decorator 语法,把装饰器作用在目标函数上。
  3. 调用目标函数

    • 实际执行的是 wrapper(),它先执行装饰逻辑,再调用原函数

三、Python 装饰器示例

1. 计时装饰器

📌 示例:在函数运行前后打印执行时间

python 复制代码
import time

# 定义装饰器
def time_logger(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 执行原函数
        end_time = time.time()  # 记录结束时间
        print(f"{func.__name__} 执行时间: {end_time - start_time:.6f} 秒")
        return result  # 返回原函数的返回值
    return wrapper  # 返回包装后的函数

# 使用装饰器
@time_logger
def slow_function():
    time.sleep(1)
    print("函数执行完毕")

slow_function()

执行流程

  1. @time_logger 作用在 slow_function 上,相当于 slow_function = time_logger(slow_function)

  2. 当调用 slow_function() 时,实际上执行的是 wrapper()

    • 记录时间 start_time
    • 调用 slow_function()
    • 最后 计算并打印执行时间。

输出示例

复制代码
函数执行完毕
slow_function 执行时间: 1.000123 秒

2. 日志装饰器

📌 示例:在函数执行前后自动记录日志

python 复制代码
def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"开始执行 {func.__name__},参数:{args} {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 执行完毕,返回值:{result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

print(add(3, 5))

执行流程

  1. @log_decoratoradd 包装成 wrapperadd 变成 wrapper 的引用。

  2. add(3, 5) 执行时:

    • 打印 "开始执行 add,参数:(3, 5) {}"
    • 运行 add(3, 5)
    • 最后 记录 "add 执行完毕,返回值:8"

输出示例

csharp 复制代码
开始执行 add,参数:(3, 5) {}
add 执行完毕,返回值:8
8

3. 组合多个装饰器

装饰器可以层层叠加,按顺序执行:

python 复制代码
@time_logger
@log_decorator
def multiply(a, b):
    time.sleep(0.5)
    return a * b

print(multiply(2, 3))

执行流程

  1. 执行 log_decorator
  2. 执行 time_logger
  3. 最后 执行 multiply

输出示例

python 复制代码
开始执行 multiply,参数:(2, 3) {}
函数执行完毕
multiply 执行时间: 0.500123 秒
multiply 执行完毕,返回值:6
6

四、JavaScript 装饰器示例

1. 计时装饰器

python 复制代码
function timeLogger(func) {
    return function (...args) {
        const startTime = Date.now();
        const result = func(...args);  // 执行原函数
        const endTime = Date.now();
        console.log(`${func.name} 执行时间: ${(endTime - startTime) / 1000} 秒`);
        return result;
    };
}

function slowFunction() {
    console.log("函数执行中...");
    for (let i = 0; i < 1e9; i++) {}  // 模拟耗时操作
    console.log("函数执行完毕");
}

const wrappedFunction = timeLogger(slowFunction);
wrappedFunction();

执行流程

  1. timeLogger(slowFunction) 返回 wrapperwrappedFunction 变成 wrapper

  2. 执行 wrappedFunction()

    • 记录 startTime
    • 执行 slowFunction()
    • 最后 计算执行时间并打印。

输出示例

python 复制代码
函数执行中...
函数执行完毕
slowFunction 执行时间: 2.345 秒

2. ES7+ 修饰器(Decorator)

在现代 JavaScript 中,可以使用 @decorator 语法

python 复制代码
function logDecorator(target, key, descriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args) {
        console.log(`执行 ${key},参数:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`返回值:`, result);
        return result;
    };
    return descriptor;
}

class MathOperations {
    @logDecorator
    add(a, b) {
        return a + b;
    }
}

const math = new MathOperations();
math.add(3, 5);

输出示例

python 复制代码
执行 add,参数:[3, 5]
返回值:8

五、装饰器模式 vs 代理模式

对比项 装饰器模式(Decorator) 代理模式(Proxy)
作用 增强对象功能 控制对象访问
是否修改原对象 ❌ 不修改 ❌ 只代理,通常不修改
是否拦截请求 ❌ 不拦截 ✅ 代理可拦截请求
典型应用 Vue reactive()、日志、计时 API 代理、缓存、权限控制

六、总结

  1. 装饰器模式用于动态扩展对象功能,而不修改其原始代码
  2. 装饰器本质是一个高阶函数,接收函数/类作为参数,返回一个增强版本
  3. Python @decorator 语法糖让装饰器更直观 ,JavaScript 也可以使用 @decorator(ES7+)。
  4. 适用于日志记录、权限控制、缓存优化等场景

🚀 掌握装饰器模式,让你的代码更优雅、更灵活!

相关推荐
code_pgf几秒前
llama.cpp 最新架构详解
人工智能·架构·llama
飞Link5 分钟前
从单模型到 Agentic AI:2026 年企业级 AI 基础设施的架构重构指南
人工智能·重构·架构
飞Link5 分钟前
巨头混战医疗 AI!OpenAI、Anthropic 与微软的 Healthcare 助理技术架构对比
人工智能·microsoft·架构
李日灐18 分钟前
【优选算法5】位运算经典算法面试题
后端·算法·面试·位运算
SamDeepThinking24 分钟前
为什么选微服务而不是动态扩容单体
java·后端·架构
uzong28 分钟前
每位工程师都必须掌握的十大数据库扩容策略
后端·架构
ayqy贾杰39 分钟前
过去三年我做对了一件事
前端·面试·ai编程
兔子零10241 小时前
当 Codex 成为主力,软件工程的重心已经变了
前端·后端·架构
悟空聊架构1 小时前
100多G数据同步引发的MySQL集群“连环炸”,我是如何一步步恢复的? - 墨天轮
后端·架构
AbelTomato1 小时前
Fiber架构、state与渲染
架构