首页 在Laravel中使用策略模式
文章
取消

在Laravel中使用策略模式

要统一多个不同但部分逻辑相似的接口成一个 API 接口,可以采用策略模式(Strategy Pattern)来实现。这种设计模式允许你定义一组算法,将它们封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于使用它的客户而变化。

步骤

  1. 定义策略接口
  2. 实现具体策略
  3. 创建策略上下文类
  4. 在控制器中使用策略上下文类
  5. 配置路由

1. 定义策略接口

首先,定义一个策略接口,所有具体的扣积分策略都将实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
<?php

namespace App\Services\Points;

use App\Models\User;
use Illuminate\Http\Request;

interface PointsStrategy
{
    public function execute(User $user, Request $request): bool;
}

2. 实现具体策略

为每个不同的扣积分操作实现具体的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

namespace App\Services\Points;

use App\Models\User;
use Illuminate\Http\Request;

class CouponDeductPointsStrategy implements PointsStrategy
{
    public function execute(User $user, Request $request): bool
    {
        // 具体的优惠券抵扣扣积分逻辑
        // 检查优惠券是否有效
        // 扣除积分
        // 记录日志
        return true;
    }
}

class EnergyCouponDeductPointsStrategy implements PointsStrategy
{
    public function execute(User $user, Request $request): bool
    {
        // 具体的元气优惠券抵扣扣积分逻辑
        // 检查元气优惠券是否有效
        // 扣除积分
        // 记录日志
        return true;
    }
}

class MiniProgramCheckInDeductPointsStrategy implements PointsStrategy
{
    public function execute(User $user, Request $request): bool
    {
        // 具体的小程序打卡扣积分逻辑
        // 检查打卡是否有效
        // 扣除积分
        // 记录日志
        return true;
    }
}

class AdminAdjustPointsStrategy implements PointsStrategy
{
    public function execute(User $user, Request $request): bool
    {
        // 具体的后台增加扣减积分逻辑
        // 检查管理员权限
        // 扣除或增加积分
        // 记录日志
        return true;
    }
}

3. 创建策略上下文类

创建一个上下文类,用于根据请求选择合适的策略并执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\Services\Points;

use Illuminate\Http\Request;
use App\Models\User;

class PointsContext
{
    protected $strategy;

    public function __construct(PointsStrategy $strategy)
    {
        $this->strategy = $strategy;
    }

    public function executeStrategy(User $user, Request $request): bool
    {
        return $this->strategy->execute($user, $request);
    }
}

4. 在控制器中使用策略上下文类

在控制器中,根据请求中的参数选择合适的策略,并通过上下文类执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Services\Points\PointsContext;
use App\Services\Points\CouponDeductPointsStrategy;
use App\Services\Points\EnergyCouponDeductPointsStrategy;
use App\Services\Points\MiniProgramCheckInDeductPointsStrategy;
use App\Services\Points\AdminAdjustPointsStrategy;
use Illuminate\Http\Request;

class PointsController extends Controller
{
    public function deductPoints(Request $request, User $user)
    {
        $type = $request->input('type'); // 请求中传入的类型参数

        switch ($type) {
            case 'coupon':
                $strategy = new CouponDeductPointsStrategy();
                break;
            case 'energy_coupon':
                $strategy = new EnergyCouponDeductPointsStrategy();
                break;
            case 'mini_program_checkin':
                $strategy = new MiniProgramCheckInDeductPointsStrategy();
                break;
            case 'admin_adjust':
                $strategy = new AdminAdjustPointsStrategy();
                break;
            default:
                return response()->json(['message' => 'Invalid type.'], 400);
        }

        $context = new PointsContext($strategy);
        $success = $context->executeStrategy($user, $request);

        if ($success) {
            return response()->json(['message' => 'Operation successful.']);
        }

        return response()->json(['message' => 'Operation failed.'], 400);
    }
}

5. 配置路由

routes/web.phproutes/api.php 中配置路由:

1
2
3
use App\Http\Controllers\PointsController;

Route::post('/points/deduct/{user}', [PointsController::class, 'deductPoints']);

总结

通过以上步骤,我们将多个不同的扣积分接口统一成一个 API 接口。我们使用策略模式来定义和实现不同的扣积分逻辑,根据请求的参数选择合适的策略,从而实现统一的接口。这样不仅减少了代码重复,还提高了代码的可维护性和可扩展性。

本文由作者按照 CC BY 4.0 进行授权