策略将一组动作行为转化为对象(动词变名词),并使它们在原始上下文对象中可互换。
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战略设计 #事件风暴 头脑会议。
如果想根据上下文动态执行对应策略,可以通过#规则引擎 实现。