1

Rust语言之GoF设计模式:中介者Mediator模式

 1 year ago
source link: https://www.jdon.com/62618
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.
neoserver,ios ssh client

Rust语言之GoF设计模式:中介者Mediator模式


中介者Mediator模式在Rust在实现很难,因为其他语言中的典型 Mediator 实现是 Rust 中的经典反模式:许多对象相互持有可变的交叉引用,试图相互变异,这在 Rust 中是一个致命的罪过——编译器不会通过你的第一个天真实施,除非它过于简单化。

根据定义,Mediator限制了对象之间的直接通信,并强制它们仅通过 mediator 对象进行协作。它也代表 MVC 模式中的控制器。

有一篇关于Rust 中的中介者模式的研究和讨论: https ://github.com/fadeevab/mediator-pattern-rust 。

交叉引用Rc<RefCell<..>>
不推荐这种方法,欺骗 Rust 编译器。
问题:

  1. 所有 trait 方法都是只读的:不可变self和不可变参数。
  2. 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);
        }
    }
}

 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK