rust-unofficial / patterns

A catalogue of Rust design patterns, anti-patterns and idioms
https://rust-unofficial.github.io/patterns/
Mozilla Public License 2.0
7.87k stars 357 forks source link

Decorator #222

Open simonsan opened 3 years ago

simonsan commented 3 years ago

Tracking issue for merging: https://github.com/lpxxn/rust-design-pattern/blob/master/structural/decorator.rs

Example:

use std::rc::Rc;

// The base Component trait defines operations that can be altered by
// decorators.
trait Component {
    fn operation(&self) -> String;
}

// Concrete Components provide default implementations of the operations.
// There might be several variations of these classes.
struct ConcreteComponent {}

impl Component for ConcreteComponent {
    fn operation(&self) -> String {
        "ConcreteComponent".to_string()
    }
}

// The base Decorator class follows the same interface as the other
// components. The primary purpose of this class is to define the wrapping
// interface for all concrete decorators. The default implementation of the
// wrapping code might include a field for storing a wrapped component and
// the means to initialize it.
trait Decorator: Component {
    fn new(component: Rc<dyn Component>) -> Self;
}

// Concrete Decorators call the wrapped object and alter its result in some
// way.
struct ConcreteDecoratorA {
    component: Rc<dyn Component>,
}

impl Decorator for ConcreteDecoratorA {
    fn new(component: Rc<dyn Component>) -> Self {
        ConcreteDecoratorA { component }
    }
}

impl Component for ConcreteDecoratorA {
    fn operation(&self) -> String {
        format!("ConcreteDecoratorA: {}", self.component.operation())
    }
}

struct ConcreteDecoratorB {
    component: Rc<dyn Component>,
}

impl Decorator for ConcreteDecoratorB {
    fn new(component: Rc<dyn Component>) -> Self {
        ConcreteDecoratorB { component }
    }
}

impl Component for ConcreteDecoratorB {
    fn operation(&self) -> String {
        format!("ConcreteDecoratorB: {}", self.component.operation())
    }
}

// The client code works with all objects using the Component interface.
// This way it can stay independent of the concrete classes of
// components it works with.
struct Client;

impl Client {
    fn client_code<T: Component>(component: &T) {
        println!("result: {}", component.operation())
    }
}

fn main() {
    let component = Rc::new(ConcreteComponent {});
    println!("client: i get a simple component: ");
    Client::client_code(component.as_ref());
    println!("client: now I've got a decorated component:");
    let decorator_a1 = ConcreteDecoratorA::new(component.clone());
    Client::client_code(&decorator_a1);

    let decorator_a2 = ConcreteDecoratorB::new(Rc::new(decorator_a1));
    Client::client_code(&decorator_a2);
}
simonsan commented 3 years ago

Tracking discussion on merging: https://github.com/lpxxn/rust-design-pattern/issues/7

simonsan commented 3 years ago

Another example can be found here: https://web.archive.org/web/20210223130852/https://chercher.tech/rust/decorator-design-pattern-rust

pickfire commented 3 years ago

URL broken.

Is the example even considered decorator? Shouldn't it be something like actix-web-codegen or rocket proc macro that looks more like a decorator?