You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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);
}

关键说明

  1. Trait扩展方法:在Specification trait中添加and/or方法,返回装箱后的复合规格(Box<dyn Specification<T>>),实现动态分发。
  2. 复合规格结构体:AndSpecification和OrSpecification持有两个子规格的装箱实例,实现Specification trait时调用子规格的判断方法并执行逻辑运算。
  3. 生命周期约束:使用'static约束确保组合后的规格拥有静态生命周期,避免悬垂引用问题。

这种方案是Rust中实现规格模式组合的惯用方式,兼顾了简洁性与灵活性,若需要更极致的性能,也可使用泛型参数实现静态分发,但会增加代码复杂度。

内容的提问来源于stack exchange,提问作者Roman Mahotskyi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:36:20