Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git на GitByBit.com! Привет! Хочешь круто подтянуть Git? Глянь мой новый курс на GitByBit.com!
Цепочка обязанностей

Цепочка обязанностей на Rust

Цепочка обязанностей — это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.

Избавляет от жёсткой привязки отправителя запроса к его получателю, позволяя выстраивать цепь из различных обработчиков динамически.

Conceptual Example

The example demonstrates processing a patient through a chain of departments. The chain of responsibility is constructed as follows:

Patient -> Reception -> Doctor -> Medical -> Cashier 

The chain is constructed using Box pointers, which means dynamic dispatch in runtime. Why? It seems quite difficult to narrow down implementation to a strict compile-time typing using generics: in order to construct a type of a full chain Rust needs full knowledge of the "next of the next" link in the chain. Thus, it would look like this:

let mut reception = Reception::<Doctor::<Medical::<Cashier>>>::new(doctor); // 😱 

Instead, Box allows chaining in any combination:

let mut reception = Reception::new(doctor); // 👍 let mut reception = Reception::new(cashier); // 🕵️‍♀️ 

patient.rs: Request

#[derive(Default)] pub struct Patient { pub name: String, pub registration_done: bool, pub doctor_check_up_done: bool, pub medicine_done: bool, pub payment_done: bool, } 

department.rs: Handlers

mod cashier; mod doctor; mod medical; mod reception; pub use cashier::Cashier; pub use doctor::Doctor; pub use medical::Medical; pub use reception::Reception; use crate::patient::Patient; /// A single role of objects that make up a chain. /// A typical trait implementation must have `handle` and `next` methods, /// while `execute` is implemented by default and contains a proper chaining /// logic. pub trait Department { fn execute(&mut self, patient: &mut Patient) { self.handle(patient); if let Some(next) = &mut self.next() { next.execute(patient); } } fn handle(&mut self, patient: &mut Patient); fn next(&mut self) -> &mut Option<Box<dyn Department>>; } /// Helps to wrap an object into a boxed type. pub fn into_next(department: impl Department + Sized + 'static) -> Option<Box<dyn Department>> { Some(Box::new(department)) } 

department/cashier.rs

use super::{Department, Patient}; #[derive(Default)] pub struct Cashier { next: Option<Box<dyn Department>>, } impl Department for Cashier { fn handle(&mut self, patient: &mut Patient) { if patient.payment_done { println!("Payment done"); } else { println!("Cashier getting money from a patient {}", patient.name); patient.payment_done = true; } } fn next(&mut self) -> &mut Option<Box<dyn Department>> { &mut self.next } } 

department/doctor.rs

use super::{into_next, Department, Patient}; pub struct Doctor { next: Option<Box<dyn Department>>, } impl Doctor { pub fn new(next: impl Department + 'static) -> Self { Self { next: into_next(next), } } } impl Department for Doctor { fn handle(&mut self, patient: &mut Patient) { if patient.doctor_check_up_done { println!("A doctor checkup is already done"); } else { println!("Doctor checking a patient {}", patient.name); patient.doctor_check_up_done = true; } } fn next(&mut self) -> &mut Option<Box<dyn Department>> { &mut self.next } } 

department/medical.rs

use super::{into_next, Department, Patient}; pub struct Medical { next: Option<Box<dyn Department>>, } impl Medical { pub fn new(next: impl Department + 'static) -> Self { Self { next: into_next(next), } } } impl Department for Medical { fn handle(&mut self, patient: &mut Patient) { if patient.medicine_done { println!("Medicine is already given to a patient"); } else { println!("Medical giving medicine to a patient {}", patient.name); patient.medicine_done = true; } } fn next(&mut self) -> &mut Option<Box<dyn Department>> { &mut self.next } } 

department/reception.rs

use super::{into_next, Department, Patient}; #[derive(Default)] pub struct Reception { next: Option<Box<dyn Department>>, } impl Reception { pub fn new(next: impl Department + 'static) -> Self { Self { next: into_next(next), } } } impl Department for Reception { fn handle(&mut self, patient: &mut Patient) { if patient.registration_done { println!("Patient registration is already done"); } else { println!("Reception registering a patient {}", patient.name); patient.registration_done = true; } } fn next(&mut self) -> &mut Option<Box<dyn Department>> { &mut self.next } } 

main.rs: Client code

mod department; mod patient; use department::{Cashier, Department, Doctor, Medical, Reception}; use patient::Patient; fn main() { let cashier = Cashier::default(); let medical = Medical::new(cashier); let doctor = Doctor::new(medical); let mut reception = Reception::new(doctor); let mut patient = Patient { name: "John".into(), ..Patient::default() }; // Reception handles a patient passing him to the next link in the chain. // Reception -> Doctor -> Medical -> Cashier. reception.execute(&mut patient); println!("\nThe patient has been already handled:\n"); reception.execute(&mut patient); } 

Output

Reception registering a patient John Doctor checking a patient John Medical giving medicine to a patient John Cashier getting money from a patient John The patient has been already handled: Patient registration is already done A doctor checkup is already done Medicine is already given to a patient Payment done 

Цепочка обязанностей на других языках программирования

Цепочка обязанностей на C# Цепочка обязанностей на C++ Цепочка обязанностей на Go Цепочка обязанностей на Java Цепочка обязанностей на PHP Цепочка обязанностей на Python Цепочка обязанностей на Ruby Цепочка обязанностей на Swift Цепочка обязанностей на TypeScript