中介者Mediator模式在Rust在实现很难,因为其他语言中的典型 Mediator 实现是 Rust 中的经典反模式:许多对象相互持有可变的交叉引用,试图相互变异,这在 Rust 中是一个致命的罪过——编译器不会通过你的第一个天真实施,除非它过于简单化。
根据定义,Mediator限制了对象之间的直接通信,并强制它们仅通过 mediator 对象进行协作。它也代表 MVC 模式中的控制器。
有一篇关于Rust 中的中介者模式的研究和讨论: https ://github.com/fadeevab/mediator-pattern-rust 。
交叉引用Rc<RefCell<..>>
不推荐这种方法,欺骗 Rust 编译器。
问题:
- 所有 trait 方法都是只读的:不可变self和不可变参数。
- Rc,RefCell在底层被广泛使用,负责从编译器到运行时的可变借用。无效的实现会导致运行时的恐慌。
自上而下的所有权
自上而下的所有权方法允许在 Rust 中应用 Mediator,因为它适用于具有严格借用检查器规则的 Rust 所有权模型。虽然不是实现 Mediator 的唯一方法,但它是一种基本比较好的方法。
关键是从所有权的角度思考:
1、中介者拥有所有组件的所有权。
2、组件不保留对中介者的引用。相反,它通过方法调用获取其引用。
// A train gets a mediator object by reference. pub trait Train { fn name(&self) -> &String; fn arrive(&mut self, mediator: &mut dyn Mediator); fn depart(&mut self, mediator: &mut dyn Mediator); }
// Mediator有提醒通知方法 has notification methods. pub trait Mediator { fn notify_about_arrival(&mut self, train_name: &str) -> bool; fn notify_about_departure(&mut self, train_name: &str); }
|
3、控制流从main.rs的fn main()中介接收外部事件/命令的地方开始。
let train1 = PassengerTrain::new("Train 1"); let train2 = FreightTrain::new("Train 2");
// Station车站有`accept`和`depart`方法。 // 但是它也实现了`Mediator'。 let mut station = TrainStation::default();
// Station车站正在接受列车的所有权。 station.accept(train1); station.accept(train2);
/ `train1`和`train2`已经被移到里面。 // 但我们可以用train的名字来 depart它们 station.depart("Train 1"); station.depart("Train 2"); station.depart("Train 3");
|
4、Mediator组件之间交互的trait方法是如notify_about_arrival, notify_about_departure通知提醒,这些动作与它用于接收外部事件的外部 API 是不同的(accept,depart是来自主循环的命令)。
车站Station的代码比较复杂:train_station.rs
下面是组件交互的动作notify_about_arrival, notify_about_departure:
#[derive(Default)] pub struct TrainStation { trains: HashMap<String, Box<dyn Train>>, train_queue: VecDeque<String>, train_on_platform: Option<String>, }
impl Mediator for TrainStation { fn notify_about_arrival(&mut self, train_name: &str) -> bool { if self.train_on_platform.is_some() { self.train_queue.push_back(train_name.into()); false } else { self.train_on_platform.replace(train_name.into()); true } }
fn notify_about_departure(&mut self, train_name: &str) { if Some(train_name.into()) == self.train_on_platform { self.train_on_platform = None;
if let Some(next_train_name) = self.train_queue.pop_front() { let mut next_train = self.trains.remove(&next_train_name).unwrap(); next_train.arrive(self); self.trains.insert(next_train_name.clone(), next_train);
self.train_on_platform = Some(next_train_name); } } } }
|
同时再实现Train组件接口trait的方法:
impl TrainStation { pub fn accept(&mut self, mut train: impl Train + 'static) { if self.trains.contains_key(train.name()) { println!("{} has already arrived", train.name()); return; }
train.arrive(self); self.trains.insert(train.name().clone(), Box::new(train)); }
pub fn depart(&mut self, name: &'static str) { let train = self.trains.remove(name); if let Some(mut train) = train { train.depart(self); } else { println!("'{}' is not on the station!", name); } } }
|