如何在Rust中实现可组合的规格模式(Specification Pattern)
Rust中规格模式的惯用实现与组合方案
需求背景
需要为WorkingDay结构体实现规格模式,包含两个基础规格:
IsActiveWorkingDaySpecification:判断工作日是否激活IsInFutureWorkingDaySpecification:判断工作日是否在未来
现有实现仅能单独使用规格,无法直接组合出And/Or这类复合规格,目标是通过链式调用(如.and()/.or())实现规格的灵活组合。
原始实现(无法组合规格)
use chrono::{NaiveDate, Utc}; use uuid::Uuid; struct WorkingDay { id: Uuid, date: NaiveDate, is_active: bool, } trait Specification<T> { fn is_satisfied_by(&self, candidate: &T) -> bool; } struct IsActiveWorkingDaySpecification; impl Specification<WorkingDay> for IsActiveWorkingDaySpecification { fn is_satisfied_by(&self, candidate: &WorkingDay) -> bool { candidate.is_active } } struct IsInFutureWorkingDaySpecification; impl Specification<WorkingDay> for IsInFutureWorkingDaySpecification { fn is_satisfied_by(&self, candidate: &WorkingDay) -> bool { Utc::now().date().naive_utc() < candidate.date } } fn main() { let working_day = WorkingDay { id: Uuid::new_v4(), date: NaiveDate::from_ymd(2077, 11, 24), is_active: true, }; let active_spec = IsActiveWorkingDaySpecification {}; let future_spec = IsInFutureWorkingDaySpecification {}; let is_active = active_spec.is_satisfied_by(&working_day); let is_in_future = future_spec.is_satisfied_by(&working_day); // 只能手动组合结果,无法直接组合规格 let is_active_and_future = is_active && is_in_future; let is_active_or_future = is_active || is_in_future; println!("IsActive: {}", is_active); println!("IsInFuture: {}", is_in_future); println!("IsActiveAndFuture: {}", is_active_and_future); }
改进实现:支持规格组合
通过为Specification trait扩展组合方法,并定义复合规格结构体,实现链式调用的规格组合:
use chrono::{NaiveDate, Utc}; use uuid::Uuid; struct WorkingDay { id: Uuid, date: NaiveDate, is_active: bool, } trait Specification<T> { fn is_satisfied_by(&self, candidate: &T) -> bool; // 组合两个规格为逻辑与 fn and(self, other: impl Specification<T> + 'static) -> Box<dyn Specification<T>> where Self: Sized + 'static, { Box::new(AndSpecification { left: Box::new(self), right: Box::new(other), }) } // 组合两个规格为逻辑或 fn or(self, other: impl Specification<T> + 'static) -> Box<dyn Specification<T>> where Self: Sized + 'static, { Box::new(OrSpecification { left: Box::new(self), right: Box::new(other), }) } } // 逻辑与复合规格 struct AndSpecification<T> { left: Box<dyn Specification<T>>, right: Box<dyn Specification<T>>, } impl<T> Specification<T> for AndSpecification<T> { fn is_satisfied_by(&self, candidate: &T) -> bool { self.left.is_satisfied_by(candidate) && self.right.is_satisfied_by(candidate) } } // 逻辑或复合规格 struct OrSpecification<T> { left: Box<dyn Specification<T>>, right: Box<dyn Specification<T>>, } impl<T> Specification<T> for OrSpecification<T> { fn is_satisfied_by(&self, candidate: &T) -> bool { self.left.is_satisfied_by(candidate) || self.right.is_satisfied_by(candidate) } } // 基础规格:判断是否激活 struct IsActiveWorkingDaySpecification; impl Specification<WorkingDay> for IsActiveWorkingDaySpecification { fn is_satisfied_by(&self, candidate: &WorkingDay) -> bool { candidate.is_active } } // 基础规格:判断是否为未来日期 struct IsInFutureWorkingDaySpecification; impl Specification<WorkingDay> for IsInFutureWorkingDaySpecification { fn is_satisfied_by(&self, candidate: &WorkingDay) -> bool { Utc::now().date().naive_utc() < candidate.date } } fn main() { let working_day = WorkingDay { id: Uuid::new_v4(), date: NaiveDate::from_ymd(2077, 11, 24), is_active: true, }; let active_spec = IsActiveWorkingDaySpecification {}; let future_spec = IsInFutureWorkingDaySpecification {}; // 使用链式调用组合规格 let is_active_and_future = active_spec .and(future_spec.clone()) .is_satisfied_by(&working_day); let is_active_or_future = active_spec .or(future_spec) .is_satisfied_by(&working_day); println!("IsActiveAndFuture: {}", is_active_and_future); println!("IsActiveOrFuture: {}", is_active_or_future); }
关键说明
- Trait扩展方法:在
Specificationtrait中添加and/or方法,返回装箱后的复合规格(Box<dyn Specification<T>>),实现动态分发。 - 复合规格结构体:
AndSpecification和OrSpecification持有两个子规格的装箱实例,实现Specificationtrait时调用子规格的判断方法并执行逻辑运算。 - 生命周期约束:使用
'static约束确保组合后的规格拥有静态生命周期,避免悬垂引用问题。
这种方案是Rust中实现规格模式组合的惯用方式,兼顾了简洁性与灵活性,若需要更极致的性能,也可使用泛型参数实现静态分发,但会增加代码复杂度。
内容的提问来源于stack exchange,提问作者Roman Mahotskyi
相关产品推荐
相关产品推荐

