装饰器模式 Decorator

通过将对象包装在内部, 来动态地更改对象在运行时的行为.

一些现代化的编程语言, 例如 Python, 有提供这样的语言特定, 比如在执行函数之前以及运行完成之后, 都打印出日志, 这样的需求就可以用装饰器模式来实现. 比如下面的例子:

# 定义一个装饰器
def log_decorator(func):
    # inner1 是一个包装函数
    def inner1():
        print("> before function execution")
        # 调用真正的函数
        func()
        print("> after function execution")

    return inner1

# 定义一个测试用的函数, 并应有上面定义好的装饰器; `@log_decorator` 等同于下面一行代码的写法:
# say_hello = log_decorator(say_hello)
@log_decorator
def hello():
    print("hello()")

# 调用函数
say_hello()

问题描述

代码示例

#![deny(
    warnings,
    clippy::all,
    clippy::cargo,
    clippy::nursery,
    clippy::pedantic
)]

pub trait Coffee {
    fn get_cost(&self) -> f64;
    fn get_description(&self) -> String;
}

pub struct SimpleCoffee {}

impl Default for SimpleCoffee {
    fn default() -> Self {
        Self::new()
    }
}

impl SimpleCoffee {
    #[must_use]
    pub const fn new() -> Self {
        Self {}
    }
}

impl Coffee for SimpleCoffee {
    #[must_use]
    fn get_cost(&self) -> f64 {
        10.0
    }

    #[must_use]
    fn get_description(&self) -> String {
        "Simple coffee".to_owned()
    }
}

pub struct MilkCoffee {
    inner: Box<dyn Coffee>,
}

impl MilkCoffee {
    #[must_use]
    pub const fn new(inner: Box<dyn Coffee>) -> Self {
        Self { inner }
    }
}

impl Coffee for MilkCoffee {
    #[must_use]
    fn get_cost(&self) -> f64 {
        self.inner.get_cost() + 2.0
    }

    #[must_use]
    fn get_description(&self) -> String {
        let mut desc = self.inner.get_description();
        desc.push_str(", milk");
        desc
    }
}

pub struct WilpCoffee {
    inner: Box<dyn Coffee>,
}

impl WilpCoffee {
    #[must_use]
    pub const fn new(inner: Box<dyn Coffee>) -> Self {
        Self { inner }
    }
}

impl Coffee for WilpCoffee {
    fn get_cost(&self) -> f64 {
        self.inner.get_cost() + 5.0
    }

    #[must_use]
    fn get_description(&self) -> String {
        let mut desc = self.inner.get_description();
        desc.push_str(", wilp");
        desc
    }
}

pub struct VanillaCoffee {
    inner: Box<dyn Coffee>,
}

impl VanillaCoffee {
    #[must_use]
    pub const fn new(inner: Box<dyn Coffee>) -> Self {
        Self { inner }
    }
}

impl Coffee for VanillaCoffee {
    fn get_cost(&self) -> f64 {
        self.inner.get_cost() + 3.0
    }

    #[must_use]
    fn get_description(&self) -> String {
        let mut desc = self.inner.get_description();
        desc.push_str(", vanilla");
        desc
    }
}

fn main() {
    let coffee = Box::new(SimpleCoffee::new());
    println!("cost: {}", coffee.get_cost());
    println!("desc: {}", coffee.get_description());

    let coffee = Box::new(MilkCoffee::new(coffee));
    println!("cost: {}", coffee.get_cost());
    println!("desc: {}", coffee.get_description());

    let coffee = Box::new(WilpCoffee::new(coffee));
    println!("cost: {}", coffee.get_cost());
    println!("desc: {}", coffee.get_description());

    let coffee = VanillaCoffee::new(coffee);
    println!("cost: {}", coffee.get_cost());
    println!("desc: {}", coffee.get_description());
}