我如何编写策略模式的 Rust 变体,其中给定结构拥有修改原始结构的策略?
How can I write this Rust variation on strategy pattern in which a given struct owns a strategy that modifies the original struct?
为复杂的标题道歉!
我是 Rust 的新手,我正在尝试解决一个非常具体的问题。 None 迄今为止我尝试过的解决方案似乎在语言范围内运行良好。
我有一个包含数据的结构(称为模型),它还包含一个用于“策略”结构的动态框。有几种不同的策略结构,它们由一个共同的特征(策略)标识,每个特征都包含通过其“执行策略”方法修改模型的单独方式。通过这样做,我可以通过更改模型的策略实例来动态更改使用的策略,如 state-machine.
每个策略都是不可变的,但它们会改变模型的数据。它们有时确实包含自己的不可变数据,因此枚举似乎不太合适。
trait Strategy {
fn execute_strategy(&self, model: &mut Model);
}
struct Strategy1;
impl Strategy for Strategy1 {
fn execute_strategy(&self, model: &mut Model) {
...
}
}
struct Strategy2;
impl Strategy for Strategy2 {
fn execute_strategy(&self, model: &mut Model) {
...
}
}
struct Model {
strategy: Box<dyn Strategy>,
}
现在我 运行 遇到的问题是执行策略。
impl Model {
fn run(&mut self) {
(*self.strategy).execute_strategy(self); // WONT COMPILE
}
}
我明白了为什么上面的代码无法编译——它看到 self 是不可变和可变地借用的。有没有惯用的解决方案?每个策略都在真空中工作,只修改模型。策略本身是不可变的,因此执行上述操作似乎是安全的。
新年快乐,在此先感谢您的帮助!
我不知道解决这个问题最惯用的方法是什么,但我能想到一些。一种方法是从 self
中删除 strategy
,这样它在操作期间就不是 self
的成员,然后再放回去。这似乎是一种反模式,因为您可能会忘记事后将其放回原处(尤其是当您忘记处理错误时):
impl Model {
fn run(&mut self) {
let strategy = std::mem::replace(&mut self.strategy, Box::new(EmptyStrategy {}));
strategy.execute_strategy(self);
let _ = std::mem::replace(&mut self.strategy, strategy);
}
}
请注意,如果您使用 Option<Box<dyn Strategy>>
,则有一个更好的 API:您可以使用 self.strategy.take()
获取(并删除)该成员,并将该成员替换为 self.strategy.replace()
(或作业)。但这并不能解决安全问题。
如果您的结构只有 1-2 个成员,更好的方法是让策略对这些成员而不是结构本身进行操作。您可以按如下方式解构结构和 运行 策略:
impl Model {
fn run(&mut self) {
let Model {
data,
data2,
strategy,
} = self;
strategy.execute_strategy(data, data2);
}
}
最通用和最安全的解决方案可能是将策略存储在 Rc
或 Arc
中,然后制作一个新的参考副本以执行该策略:
struct Model {
strategy: Arc<Box<dyn Strategy>>,
}
impl Model {
fn run(&mut self) {
self.strategy.clone().execute_strategy(self);
}
}
为复杂的标题道歉!
我是 Rust 的新手,我正在尝试解决一个非常具体的问题。 None 迄今为止我尝试过的解决方案似乎在语言范围内运行良好。
我有一个包含数据的结构(称为模型),它还包含一个用于“策略”结构的动态框。有几种不同的策略结构,它们由一个共同的特征(策略)标识,每个特征都包含通过其“执行策略”方法修改模型的单独方式。通过这样做,我可以通过更改模型的策略实例来动态更改使用的策略,如 state-machine.
每个策略都是不可变的,但它们会改变模型的数据。它们有时确实包含自己的不可变数据,因此枚举似乎不太合适。
trait Strategy {
fn execute_strategy(&self, model: &mut Model);
}
struct Strategy1;
impl Strategy for Strategy1 {
fn execute_strategy(&self, model: &mut Model) {
...
}
}
struct Strategy2;
impl Strategy for Strategy2 {
fn execute_strategy(&self, model: &mut Model) {
...
}
}
struct Model {
strategy: Box<dyn Strategy>,
}
现在我 运行 遇到的问题是执行策略。
impl Model {
fn run(&mut self) {
(*self.strategy).execute_strategy(self); // WONT COMPILE
}
}
我明白了为什么上面的代码无法编译——它看到 self 是不可变和可变地借用的。有没有惯用的解决方案?每个策略都在真空中工作,只修改模型。策略本身是不可变的,因此执行上述操作似乎是安全的。
新年快乐,在此先感谢您的帮助!
我不知道解决这个问题最惯用的方法是什么,但我能想到一些。一种方法是从 self
中删除 strategy
,这样它在操作期间就不是 self
的成员,然后再放回去。这似乎是一种反模式,因为您可能会忘记事后将其放回原处(尤其是当您忘记处理错误时):
impl Model {
fn run(&mut self) {
let strategy = std::mem::replace(&mut self.strategy, Box::new(EmptyStrategy {}));
strategy.execute_strategy(self);
let _ = std::mem::replace(&mut self.strategy, strategy);
}
}
请注意,如果您使用 Option<Box<dyn Strategy>>
,则有一个更好的 API:您可以使用 self.strategy.take()
获取(并删除)该成员,并将该成员替换为 self.strategy.replace()
(或作业)。但这并不能解决安全问题。
如果您的结构只有 1-2 个成员,更好的方法是让策略对这些成员而不是结构本身进行操作。您可以按如下方式解构结构和 运行 策略:
impl Model {
fn run(&mut self) {
let Model {
data,
data2,
strategy,
} = self;
strategy.execute_strategy(data, data2);
}
}
最通用和最安全的解决方案可能是将策略存储在 Rc
或 Arc
中,然后制作一个新的参考副本以执行该策略:
struct Model {
strategy: Arc<Box<dyn Strategy>>,
}
impl Model {
fn run(&mut self) {
self.strategy.clone().execute_strategy(self);
}
}