PHP规约模式验证业务规则吗

wen PHP项目 1

本文目录导读:

PHP规约模式验证业务规则吗

  1. 规约模式 vs. 传统验证方法
  2. 核心概念
  3. PHP 示例
  4. 优势总结
  5. 适用场景
  6. 不适合的场景

是的,PHP 中规约模式非常适合用于验证业务规则。

规约模式(Specification Pattern)的核心思想是将业务规则封装成独立的、可组合的对象,它不仅仅是数据验证(如字段格式、非空),而是专门用于处理复杂的业务逻辑判断

下面详细解释规约模式如何应用于业务规则验证,并提供一个 PHP 示例。


规约模式 vs. 传统验证方法

特性 传统验证(如 if-else、Laravel FormRequest) 规约模式
关注点 数据格式、字段完整性(输入验证) 业务逻辑、领域规则(如:用户是否可以借款、订单是否可取消)
复用性 低,通常写在控制器或请求类中,难跨场景复用 高,每个规则都是独立对象,可在不同地方组合使用
可读性 复杂业务逻辑下,if-else 嵌套难读 逻辑清晰,语义化强($isEligible = $canBorrow->and($isInGoodStanding)
变化性 规则变化需修改原有代码 规则变化只需修改对应规约对象或组合方式
组合性 难组合,逻辑硬编码 天然支持逻辑运算(and, or, not

一句话总结: 传统验证检查输入“对不对”,规约模式检查领域“行不行”。


核心概念

  • 规约接口:定义核心方法 isSatisfiedBy($candidate),返回 bool
  • 具体规约:实现接口,封装单个业务规则。
  • 组合规约:通过 AndSpecificationOrSpecificationNotSpecification 等,将多个简单规约组合成复杂的业务规则。

PHP 示例

假设我们有一个贷款审批系统,需要验证一个用户是否有资格申请贷款,规则如下:

  1. 用户年龄必须在 18-65 岁之间。
  2. 用户月收入必须大于 5000。
  3. 用户信用评分必须大于 700。

步骤 1:定义规约接口

<?php
interface Specification
{
    public function isSatisfiedBy($candidate): bool;
}

步骤 2:实现具体规约

<?php
class User
{
    public function __construct(
        public readonly int $age,
        public readonly float $monthlyIncome,
        public readonly int $creditScore
    ) {}
}
class AgeSpecification implements Specification
{
    public function isSatisfiedBy($user): bool
    {
        return $user->age >= 18 && $user->age <= 65;
    }
}
class IncomeSpecification implements Specification
{
    public function __construct(private readonly float $minIncome = 5000) {}
    public function isSatisfiedBy($user): bool
    {
        return $user->monthlyIncome >= $this->minIncome;
    }
}
class CreditScoreSpecification implements Specification
{
    public function __construct(private readonly int $minScore = 700) {}
    public function isSatisfiedBy($user): bool
    {
        return $user->creditScore >= $this->minScore;
    }
}

步骤 3:实现组合规约(让规则可以组合)

<?php
class AndSpecification implements Specification
{
    /** @param Specification[] $specifications */
    public function __construct(private readonly array $specifications) {}
    public function isSatisfiedBy($candidate): bool
    {
        foreach ($this->specifications as $spec) {
            if (!$spec->isSatisfiedBy($candidate)) {
                return false;
            }
        }
        return true;
    }
}
class OrSpecification implements Specification
{
    /** @param Specification[] $specifications */
    public function __construct(private readonly array $specifications) {}
    public function isSatisfiedBy($candidate): bool
    {
        foreach ($this->specifications as $spec) {
            if ($spec->isSatisfiedBy($candidate)) {
                return true;
            }
        }
        return false;
    }
}
class NotSpecification implements Specification
{
    public function __construct(private readonly Specification $specification) {}
    public function isSatisfiedBy($candidate): bool
    {
        return !$this->specification->isSatisfiedBy($candidate);
    }
}

步骤 4:使用规约进行业务判断

<?php
// 创建用户对象(待判断的数据)
$user = new User(age: 28, monthlyIncome: 8000, creditScore: 750);
// 创建具体规约
$ageSpec = new AgeSpecification();
$incomeSpec = new IncomeSpecification(minIncome: 5000);
$creditSpec = new CreditScoreSpecification(minScore: 700);
// 组合规则:必须同时满足(AND)
$loanApprovalRule = new AndSpecification([$ageSpec, $incomeSpec, $creditSpec]);
// 执行验证
if ($loanApprovalRule->isSatisfiedBy($user)) {
    echo "用户符合贷款条件。"; // 输出这条
} else {
    echo "用户不符合贷款条件。";
}
// ---------- 高级用法:灵活组合 ----------
// 规则改变:年龄或信用评分满足其一即可(OR),但收入必须达标(AND)
$flexibleRule = new AndSpecification([
    $incomeSpec, // 收入必须达标
    new OrSpecification([$ageSpec, $creditSpec]) // 年龄或信用满足其一
]);
if ($flexibleRule->isSatisfiedBy($user)) {
    echo "用户符合灵活贷款条件。";
} else {
    echo "用户不符合灵活贷款条件。";
}
// 排除指定情况:年龄18-65,收入大于5000,且信用**不**低于700
$excludeRule = new AndSpecification([
    $ageSpec,
    $incomeSpec,
    new NotSpecification(new CreditScoreSpecification(minScore: 700)) // 注意:这里语义可能是“信用分不高于700” -> 实际业务需调整
]);
// 更恰当的写法:信用分不低于700 = 信用分 >= 700,上面的写法是“不满足信用分>=700”即“信用分<700”
// 所以直接使用 CreditScoreSpecification 即可,这里仅为演示 NotSpecification
$correctExcludeRule = new AndSpecification([
    $ageSpec,
    $incomeSpec,
    $creditSpec // 直接包含即可,表示信用达标
]);

优势总结

  1. 业务逻辑清晰$rule = new AndSpecification([$ageSpec, $incomeSpec])if ($age >= 18 && $age <= 65 && $income > 5000) 更语义化。
  2. 高复用性AgeSpecification 可以在贷款、租房、会员注册等多个场景复用。
  3. 易于扩展:新增一个“居住城市”规则,只需新增 CitySpecification,然后在组合中加入即可,无需修改原有规约。
  4. 避免“规则爆炸”:不用为每种业务组合编写一个独立方法,而是通过组合规约动态生成。
  5. 适合复杂场景:当业务规则经常变化(如运营活动条件频繁调整),或规则数量很多时,规约模式的维护成本远低于硬编码。

适用场景

  • 领域驱动设计 (DDD):作为领域层的一部分,封装聚合根或实体的业务规则。
  • 复杂过滤/查询:例如在开发工作中,根据用户角色、部门、状态、创建时间等组合条件筛选待办任务。
  • 工作流/审批系统:判断某个环节是否可以自动通过。
  • 权限/策略校验:判断用户是否有执行特定操作的权限。

不适合的场景

  • 简单的表单输入验证(如非空、邮箱格式、字符串长度),这种情况使用 PHP 的 filter_var()validator 组件(如 Laravel 的 Validator)或 symfony/validator 更直接。
  • 只有 1-2 个固定规则,且未来几乎不变,此时硬编码更简单。

是的,规约模式非常适合验证业务规则。 它让你的业务逻辑代码更健壮、更灵活、更易于维护,尤其在面对复杂的、多变的、需要组合的业务规则时,规约模式是一个极其强大的工具,如果你正在开发一个业务逻辑较复杂的 PHP 项目(如 ERP、CRM、金融系统),引入规约模式会显著提升代码质量。

抱歉,评论功能暂时关闭!