告别 "if-else 地狱":PHP 中的策略模式、管道模式与责任链模式实战

告别 "if-else 地狱":PHP 中的策略模式、管道模式与责任链模式实战

"代码一开始很干净,直到产品经理说:'加一个条件判断就行'。"

如果你写过超过两年的 PHP 业务代码,一定见过这样的场景:一个 processOrder() 方法里塞满了层层嵌套的 if-elseif-else,像一棵不断生长的荆棘树,每次改动都提心吊胆,生怕哪根刺扎到旧逻辑。

这就是传说中的 "if-else 地狱" 。今天我们用三个设计模式------策略模式、管道模式、责任链模式------来拆掉这棵荆棘树,用 PHP 实战案例展示每种模式的适用场景和落地方式。


一、if-else 地狱长什么样?

先看一个典型的"地狱级"代码:

php 复制代码
// ❌ 地狱级 if-else
public function handleOrder(Order $order)
{
    if ($order->type === 'normal') {
        if ($order->amount > 1000) {
            // 大额普通订单逻辑
            if ($order->user->isVip()) {
                // VIP 大额...
            } else {
                // 普通大额...
            }
        } else {
            // 小额普通订单
            if ($order->coupon) {
                // 有优惠券...
            }
        }
    } elseif ($order->type === 'preorder') {
        // 预售订单...
    } elseif ($order->type === 'group') {
        // 拼团订单...
    } elseif ($order->type === 'flash') {
        // 秒杀订单...
    } else {
        throw new \Exception('未知订单类型');
    }
}

问题在哪?

痛点 说明
违反开闭原则 每加一种订单类型就要改这个方法
违反单一职责 一个方法承担了所有类型的处理逻辑
难以测试 要覆盖所有分支组合,测试用例爆炸
可读性差 嵌套层级深,新人看了直接懵

二、策略模式:让每种算法各就各位

核心思想

定义一系列算法,把它们封装起来,使它们可以互相替换。

策略模式最适合解决 "同一行为,不同实现" ​ 的场景。比如:订单处理、支付方式、运费计算、折扣策略等。

实战:订单处理策略

1. 定义策略接口
php 复制代码
interface OrderStrategy
{
    public function handle(Order $order): OrderResult;
    public function supports(string $type): bool;
}
2. 实现具体策略
php 复制代码
class NormalOrderStrategy implements OrderStrategy
{
    public function supports(string $type): bool
    {
        return $type === 'normal';
    }

    public function handle(Order $order): OrderResult
    {
        // 普通订单的处理逻辑,干净、专注
        if ($order->amount > 1000) {
            return $this->handleLargeOrder($order);
        }
        return $this->handleRegularOrder($order);
    }

    private function handleLargeOrder(Order $order): OrderResult
    {
        // 大额逻辑...
    }

    private function handleRegularOrder(Order $order): OrderResult
    {
        // 普通逻辑...
    }
}
php 复制代码
class GroupOrderStrategy implements OrderStrategy
{
    public function supports(string $type): bool
    {
        return $type === 'group';
    }

    public function handle(Order $order): OrderResult
    {
        // 拼团订单的处理逻辑,完全独立
        // 就算这里改崩了,也不影响其他订单类型
    }
}
php 复制代码
class FlashOrderStrategy implements OrderStrategy
{
    public function supports(string $type): bool
    {
        return $type === 'flash';
    }

    public function handle(Order $order): OrderResult
    {
        // 秒杀订单的处理逻辑
    }
}
3. 策略上下文(调度器)
php 复制代码
class OrderStrategyContext
{
    /** @var OrderStrategy[] */
    private array $strategies;

    public function __construct(iterable $strategies)
    {
        $this->strategies = $strategies instanceof Traversable 
            ? iterator_to_array($strategies) 
            : $strategies;
    }

    public function handle(Order $order): OrderResult
    {
        foreach ($this->strategies as $strategy) {
            if ($strategy->supports($order->type)) {
                return $strategy->handle($order);
            }
        }

        throw new \InvalidArgumentException("不支持的订单类型: {$order->type}");
    }
}
4. 在 Laravel 中使用(依赖注入)
php 复制代码
// AppServiceProvider.php
public function register()
{
    $this->app->tag([
        NormalOrderStrategy::class,
        GroupOrderStrategy::class,
        FlashOrderStrategy::class,
        PreorderStrategy::class,
    ], 'order_strategies');

    $this->app->bind(OrderStrategyContext::class, function ($app) {
        return new OrderStrategyContext(
            $app->tagged('order_strategies')
        );
    });
}
php 复制代码
// Controller 中
public function process(Order $order, OrderStrategyContext $context)
{
    $result = $context->handle($order);
    return response()->json($result);
}

策略模式效果对比

scss 复制代码
之前:一个 200 行的 handleOrder(),改一处怕全局
之后:每个 Strategy 独立文件,新增类型只需加一个类
维度 之前 之后
新增订单类型 修改原有方法 新增一个类
测试 需要 mock 所有分支 每个策略独立测试
代码审查 要通读整个方法 只看新增的策略类
新人上手 先理解所有分支 看对应类型即可

三、管道模式:像流水线一样处理请求

核心思想

将复杂的处理过程拆解为一系列独立的步骤,数据像水流一样依次穿过每个步骤。

管道模式(Pipeline)特别适合 "一个请求需要经过多个处理步骤" ​ 的场景:请求中间件、数据过滤/转换、表单验证链、订单处理流水线等。

Laravel 框架本身大量使用管道模式------中间件就是最典型的例子。

实战:订单数据处理管道

假设一个订单创建时,需要经过:参数校验 → 数据补全 → 价格计算 → 库存锁定 → 持久化。

1. 定义管道中间件接口
php 复制代码
interface OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext;
}
2. 实现各个管道环节
php 复制代码
class ValidateOrderPipe implements OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext
    {
        $data = $context->getData();

        if (empty($data['product_id'])) {
            throw new \InvalidArgumentException('商品ID不能为空');
        }

        if ($data['quantity'] <= 0) {
            throw new \InvalidArgumentException('购买数量必须大于0');
        }

        return $next($context);
    }
}
php 复制代码
class EnrichOrderPipe implements OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext
    {
        $data = $context->getData();

        // 补充商品信息
        $product = Product::find($data['product_id']);
        $context->set('product', $product);
        $context->set('unit_price', $product->price);

        return $next($context);
    }
}
php 复制代码
class CalculatePricePipe implements OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext
    {
        $total = $context->get('unit_price') * $context->get('quantity');

        // 应用折扣
        $discount = $this->calculateDiscount($context);
        $context->set('discount', $discount);
        $context->set('total_amount', $total - $discount);

        return $next($context);
    }

    private function calculateDiscount(OrderContext $context): float
    {
        // 折扣逻辑...
        return 0;
    }
}
php 复制代码
class LockInventoryPipe implements OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext
    {
        $product = $context->get('product');
        $quantity = $context->get('quantity');

        if ($product->stock < $quantity) {
            throw new \RuntimeException('库存不足');
        }

        // 锁定库存(Redis 或数据库行锁)
        Inventory::lock($product->id, $quantity);

        return $next($context);
    }
}
php 复制代码
class PersistOrderPipe implements OrderPipe
{
    public function handle(OrderContext $context, \Closure $next): OrderContext
    {
        $order = Order::create([
            'user_id' => $context->get('user_id'),
            'product_id' => $context->get('product_id'),
            'quantity' => $context->get('quantity'),
            'total_amount' => $context->get('total_amount'),
            'status' => 'pending',
        ]);

        $context->set('order', $order);

        return $next($context);
    }
}
3. 管道调度器
php 复制代码
class OrderPipeline
{
    /** @var OrderPipe[] */
    private array $pipes;

    public function __construct(array $pipes)
    {
        $this->pipes = $pipes;
    }

    public function process(OrderContext $context): OrderContext
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes),
            function ($next, OrderPipe $pipe) {
                return function ($context) use ($pipe, $next) {
                    return $pipe->handle($context, $next);
                };
            },
            function ($context) {
                return $context; // 最终闭包
            }
        );

        return $pipeline($context);
    }
}
4. 使用
ini 复制代码
$pipeline = new OrderPipeline([
    new ValidateOrderPipe(),
    new EnrichOrderPipe(),
    new CalculatePricePipe(),
    new LockInventoryPipe(),
    new PersistOrderPipe(),
]);

$context = new OrderContext($request->validated());
$result = $pipeline->process($context);

$order = $result->get('order');

Laravel 原生 Pipeline 写法

如果你用 Laravel,其实框架已经内置了 Pipeline:

php 复制代码
use Illuminate\Pipeline\Pipeline;

$order = app(Pipeline::class)
    ->send($context)
    ->through([
        ValidateOrderPipe::class,
        EnrichOrderPipe::class,
        CalculatePricePipe::class,
        LockInventoryPipe::class,
        PersistOrderPipe::class,
    ])
    ->then(function ($context) {
        return $context->get('order');
    });

管道模式效果

scss 复制代码
之前:一个 createOrder() 方法 150 行,校验/计算/持久化混在一起
之后:每个 Pipe 只做一件事,可以随意组合、复用、调整顺序

四、责任链模式:让请求找到它的处理者

核心思想

使多个对象都有机会处理请求,从而避免发送者和接收者的耦合。将对象连成一条链,请求沿着链传递,直到有一个对象处理它。

责任链模式适合 "多个处理器,谁有能力谁处理" ​ 的场景:审批流、客服工单分配、异常处理、中间件链。

实战:订单审批流程

不同金额的订单需要不同级别的审批:

金额范围 审批人
≤ 1000 组长
1000 ~ 5000 经理
5000 ~ 20000 总监
> 20000 CEO
1. 定义审批处理器
php 复制代码
abstract class Approver
{
    protected ?Approver $next = null;

    public function setNext(Approver $approver): Approver
    {
        $this->next = $approver;
        return $approver;
    }

    abstract public function approve(Order $order): ?ApprovalResult;
}
2. 实现各级审批人
scala 复制代码
class TeamLeadApprover extends Approver
{
    public function approve(Order $order): ?ApprovalResult
    {
        if ($order->amount <= 1000) {
            return new ApprovalResult('approved', '组长审批通过');
        }

        if ($this->next) {
            return $this->next->approve($order);
        }

        return null;
    }
}
php 复制代码
class ManagerApprover extends Approver
{
    public function approve(Order $order): ?ApprovalResult
    {
        if ($order->amount <= 5000) {
            // 经理额外逻辑:检查预算
            if ($this->checkBudget($order)) {
                return new ApprovalResult('approved', '经理审批通过');
            }
            return new ApprovalResult('rejected', '超出部门预算');
        }

        if ($this->next) {
            return $this->next->approve($order);
        }

        return null;
    }

    private function checkBudget(Order $order): bool
    {
        // 预算检查逻辑...
        return true;
    }
}
scala 复制代码
class DirectorApprover extends Approver
{
    public function approve(Order $order): ?ApprovalResult
    {
        if ($order->amount <= 20000) {
            return new ApprovalResult('approved', '总监审批通过');
        }

        if ($this->next) {
            return $this->next->approve($order);
        }

        return null;
    }
}
scala 复制代码
class CEOApprover extends Approver
{
    public function approve(Order $order): ?ApprovalResult
    {
        // CEO 处理所有剩余情况
        return new ApprovalResult('approved', 'CEO 审批通过');
    }
}
3. 组装责任链
php 复制代码
class ApprovalChain
{
    private Approver $chain;

    public function __construct()
    {
        $teamLead = new TeamLeadApprover();
        $manager = new ManagerApprover();
        $director = new DirectorApprover();
        $ceo = new CEOApprover();

        // 组装链条:组长 → 经理 → 总监 → CEO
        $teamLead->setNext($manager);
        $manager->setNext($director);
        $director->setNext($ceo);

        $this->chain = $teamLead;
    }

    public function process(Order $order): ApprovalResult
    {
        $result = $this->chain->approve($order);

        if (!$result) {
            throw new \RuntimeException('无人能审批此订单');
        }

        return $result;
    }
}
4. 使用
ini 复制代码
$chain = new ApprovalChain();
$result = $chain->process($order);

echo $result->getMessage(); // "经理审批通过"

责任链模式效果

php 复制代码
之前:if ($amount <= 1000) { ... } elseif ($amount <= 5000) { ... } else { ... }
之后:新增审批级别只需加一个类,调整金额阈值只改对应类

五、三种模式怎么选?

这是最关键的问题。三个模式都能消除 if-else,但适用场景不同:

模式 核心隐喻 适合场景 关键特征
策略模式 菜单点餐 同一行为的不同算法实现 互斥选择,选一个策略执行
管道模式 工厂流水线 一个请求经过多道工序 顺序执行,每步都参与
责任链模式 击鼓传花 多个处理器,谁行谁上 链式传递,一个处理就停

决策树

arduino 复制代码
你的 if-else 在做什么?
│
├─ 根据类型选择不同的处理方式 → 策略模式
│   └─ 例:支付网关选择、运费计算、导出格式
│
├─ 一个请求需要依次经过多个处理步骤 → 管道模式
│   └─ 例:表单处理、订单创建流程、API 请求中间件
│
└─ 多个处理者,按条件逐级判断谁能处理 → 责任链模式
    └─ 例:审批流、异常处理器、日志级别处理

组合使用

实际项目中,这三种模式经常组合出现。比如一个完整的订单系统:

arduino 复制代码
┌─────────────────────────────────────────────┐
│            OrderService::create()            │
│                                             │
│  1. 管道模式:数据校验 → 补全 → 计算 → 持久化 │
│       │                                     │
│       ├─ 计算价格步骤内部 → 策略模式          │
│       │   ├─ VIP 价格策略                    │
│       │   ├─ 促销价格策略                    │
│       │   └─ 普通价格策略                    │
│       │                                     │
│       └─ 持久化后 → 责任链模式触发通知        │
│           ├─ 短信通知                        │
│           ├─ 邮件通知                        │
│           └─ App 推送                        │
└─────────────────────────────────────────────┘

六、总结

设计模式不是银弹,但它是你对抗代码腐烂的武器库。

你获得的能力 说明
消除 if-else 地狱 不再有 5 层嵌套的条件判断
拥抱开闭原则 新增功能靠"加"而不是靠"改"
代码可测试性 每个类独立测试,mock 成本极低
团队协作友好 每个人改自己的类,减少冲突

最后一句忠告 :不要为了用模式而用模式。如果一个 if-else 只有两个分支且永远不会变,那就老老实实写 if-else。设计模式的目的是管理复杂度 ,而不是制造过度设计

相关推荐
面试鸭1 小时前
Codex 跑进法务部,周活居然暴涨了 108 倍???
后端·面试·求职
Larcher2 小时前
从 SDD 到可交付:我如何用规范驱动 AI 做出一个 Chrome 翻译插件
前端·后端·架构
choumou_M2 小时前
SpringBoot_5:商品秒杀场景的并发实践(初步)
java·spring boot·后端
陈明勇2 小时前
Go 1.27 重磅发布:泛型方法落地、分配性能跃升,JSON v2 正式可用
后端
Zane19943 小时前
create_task 和直接 await 到底有什么不一样?asyncio 的 Task、gather 与并发数量控制
后端·python
喜欢吃豆3 小时前
5 分钟,让网页里站着一个会说话的 3D 数字人
后端
Python私教3 小时前
创业团队做管理系统,别先堆页面:我把 14 个问题做成了需求门禁
后端·python·架构
攻城有术3 小时前
专项攻克-SpringBoot启动后 Bean的实例数量问题
java·spring boot·后端
JimmtButler3 小时前
功能明明正常,架构为什么还是会腐化?<第一章>
后端·架构