Laravel的单一责任原则(SRP)

SRP原则(一种责任原则)是编写支持代码的基本原则之一。在本文中,我将通过PHP语言和Laravel框架示例演示如何应用此原理。

通常,在描述MVC开发模型时,会给控制器分配不合理的大任务。获取参数,业务逻辑,授权和响应。

当然,在文章和书籍中将其作为示例进行描述,但通常被认为是工作项目中采取行动的呼吁。这样的方法将不可避免地导致类的增长不受控制,并使代码支持大大复杂化。

分担责任原则


举个例子,我们将采用比较粗略但经常遇到的“ thick”控制器类型。

class OrderController extends Controller
{
    public function create(Request $request)
    {
        $rules = [
            'product_id' => 'required|max:10',
            'count' => 'required|max:10',
        ];
        $validator = Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            return response()->json($validator->errors(), 400);
        }

         //  
        $order = Order::create($request->all());

        //   

        //  sms    

        return response()->json($order, 201);
    }
}

在此示例中,很明显,控制员对“下订单”了解得太多,还承担了通知买方和保留货物的任务。

我们将努力确保控制器仅需控制整个过程,而无需业务逻辑。

首先,我们将在单独的Request类中验证参数。

class OrderRequest extends Request
{
    public function rules(): array
    {
         return [
             'product_id' => 'required|max:10',
             'count' => 'required|max:10',
         ];    
    }
}

并将所有业务逻辑移至OrderService类

public class OrderService
{
    public function create(array $params)
    {
        //  

        //  

        //     sms
        $sms = new RuSms($appId);

        //  ,    
        $sms->send($number, $text);
     }
}

使用服务容器,我们将在控制器中实现我们的服务。

结果,我们有了一个“瘦”控制器。

class OrderController extends Controller
{
    protected $service;
	
    public function __construct(OrderService $service)
    {
	$this->service = $service;	
    }

    public function create(OrderRequest $request)
    {
        $this->service->create($request->all());

        return response()->noContent(201);
    }
}

已经更好了。所有业务逻辑都已移交给服务。控制器仅知道OrderRequestOrderService类 -完成其工作所需的最少信息集。

但是现在我们的服务也需要重构。让我们拿出发送短信到一个单独的类的逻辑。

class SmsRu
{
    protected $appId;

    public function __constructor(string $appId)
    {
        $this->appId = $appId;
    }

    public function send($number, $text): void
    {
          //     
    }
}    

并通过构造函数实现

class OrderService
{
    private $sms;

    public function __construct()
    {
	$this->sms = new SmsRu($appId);	
    }

    public function create(array $params): void
    {
          //  

          //  

          //  ,    
          $this->sms->send($nubmer, $text);
    }
}    

已经更好了,但是OrderService仍然对发送消息了解得太多。将来我们可能需要替换消息传递提供程序或添加单元测试。我们通过添加SmsSender接口继续重构,并通过SmsServiceProvider提供程序指定SmsRu

interface SmsSenderInterface
{
    public function send($number, $text): void;
}

class SmsServiceProvider implements ServiceProviderInterface
{
    public function register(): void
    {
        $this->app->singleton(SmsSenderInterface::class, function ($app) {
            return new SmsRu($params['app_id']);
        });
    }
}

现在,该服务也摆脱了不必要的细节

class OrderService
{
    private $sms;

    public function __construct(SmsSenderInterface $sms)
    {
	$this->sms = $sms;	
    }

    public function create(): void
    {
          //  

          //  

          //  ,    
          $this->sms->send($nubmer, $text);
    }
}    

事件驱动架构


发送消息不是基本订单创建过程的一部分。不管发送的消息如何,都会创建订单,也有可能在将来添加取消短信通知的选项。为了不使OrderService带有不必要的通知详细信息,可以使用Laravel Observers一个类,它将跟踪具有我们Order模型的某些行为的事件,并为其分配所有客户通知逻辑。

class OrderObserver
{
    private $sms;

    public function __construct(SmsSenderInterface $sms)
    {
	$this->sms = $sms;	
    }

    /**
     * Handle the Order "created" event.
     */
    public function created(Order $order)
    {
        $this->sms->send($nubmer, $text);
    }

不要忘记AppServiceProvider中注册OrderObserver

结论


我知道我在这篇文章中描述的是相当平庸的事情,但是我想展示如何在Laravel框架上实现这一原理。

SPR原则是最重要的原则之一,为其余部分铺平了道路。需要使用它,尤其是因为Laravel框架提供了所有必需的工具。

All Articles