3

Rust语言之GoF设计模式:策略模式

 1 year ago
source link: https://www.jdon.com/62622
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Rust语言之GoF设计模式:策略模式


策略将一组动作行为转化为对象(动词变名词),并使它们在原始上下文对象中可互换。

Rust实现
创建一个表示公共接口的trait并多次实现该trait:

trait Strategy {
    fn execute(&self);
}

struct ConcreteStrategyA;

impl Strategy for ConcreteStrategyA {
    fn execute(&self) {
        println!("ConcreteStrategyA")
    }
}

struct ConcreteStrategyB;

impl Strategy for ConcreteStrategyB {
    fn execute(&self) {
        println!("ConcreteStrategyB")
    }
}

然后实现持有一个实现该trait的泛型类型,Context上下文是一个持有trait实现的数据结构:

struct Context<S> {
    strategy: S,
}

impl<S> Context<S>
where
    S: Strategy,
{
    fn do_things(&self) {
        println!("Common preamble");
        self.strategy.execute();
        println!("Common postamble");
    }
}

知识点:

泛型类型、特征trait和生命周期

  • Rust中泛型:不是像i32或String之类的具体类型。我们可以表达泛型的行为或它们与其他泛型的关系,而不知道编译和运行代码时它们的位置。
    泛型可以为编译器提供有关引用如何相互关联的信息
  • 生命周期允许我们向编译器提供有关借用值的足够信息,以便它可以确保引用在更多情况下有效
  • 可以将接口trait特征与泛型类型结合起来,将泛型类型限制为仅接受具有特定行为的类型,而不是仅接受任何类型。

调用代码:

fn main() {
    let a = Context {
        strategy: ConcreteStrategyA,
    };
    a.do_things();

    println!("\n---\n");

    let b = Context {
        strategy: ConcreteStrategyB,
    };
    b.do_things();
}

第二个Rust案例
函数和闭包简化了策略实现,因为您可以将行为直接注入对象而无需复杂的接口定义。

type RouteStrategy = fn(from: &str, to: &str);

fn walking_strategy(from: &str, to: &str) {
    println!("Walking route from {} to {}: 4 km, 30 min", from, to);
}

fn public_transport_strategy(from: &str, to: &str) {
    println!(
        "Public transport route from {} to {}: 3 km, 5 min",
        from, to
    );
}

struct Navigator {
    route_strategy: RouteStrategy,
}

impl Navigator {
    pub fn new(route_strategy: RouteStrategy) -> Self {
        Self { route_strategy }
    }

    pub fn route(&self, from: &str, to: &str) {
        (self.route_strategy)(from, to);
    }
}

fn main() {
    let navigator = Navigator::new(walking_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(public_transport_strategy);
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");

    let navigator = Navigator::new(|from, to| println!("Specific route from {} to {}", from, to));
    navigator.route("Home", "Club");
    navigator.route("Club", "Work");
}

似乎 Strategy 经常被隐含地广泛使用在 Rust 的现代开发中,例如它就像迭代器一样工作:

let a = [0i32, 1, 2];

let mut iter = a.iter().filter(|x| x.is_positive());

策略模式也是过滤器的一种实现,不过这种过滤器是一种策略过滤器,非常类似decorator模式,只是不依赖附加于一个固定对象,不同于职责链模式,每个策略过滤器只能有一个策略有效,如果多个策略依次有效,就是策略模式+责任链模式了。
策略模式不同于命令模式,命令模式中任何一个命令的发生都是事先无法控制预测的,而策略模式中策略激活都是事先谋划计划好的,如同诸葛亮的锦囊妙计,需要在特定上下文打开激活,这些策略都是事先计划好放入袋子中,当然料事如神诸葛亮可以这么干,一般人计划能力没有这么强,而且会产生认知心理焦虑,控制力太强并不有益于身心健康。

事先计划设计是一件很繁重的活动,见DDD战略设计 事件风暴 头脑会议。

如果想根据上下文动态执行对应策略,可以通过规则引擎 实现。
 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK