مبدأ المسؤولية الفردية (SRP) مع Laravel

مبدأ SRP (مبدأ المسؤولية واحد) هو أحد المبادئ الأساسية لكتابة التعليمات البرمجية المدعومة. في هذه المقالة ، سأوضح كيفية تطبيق هذا المبدأ باستخدام مثال لغة PHP وإطار Laravel.

في كثير من الأحيان ، عند وصف نموذج تطوير MVC ، يتم تعيين المهام الكبيرة بشكل غير معقول إلى وحدة التحكم. الحصول على المعلمات ومنطق الأعمال والتفويض والاستجابة.

بالطبع ، يتم وصف هذا في المقالات والكتب كمثال ، ولكن غالبًا ما يُنظر إليه على أنه دعوة للعمل في مشاريع العمل. سيؤدي مثل هذا النهج حتمًا إلى نمو الصفوف غير المنضبط ويزيد من تعقيد دعم التعليمات البرمجية.

مبدأ المسؤولية المشتركة


على سبيل المثال ، سنأخذ نوعًا صعبًا إلى حد ما ، ولكن غالبًا ما نلتقي بنوع وحدة التحكم "السميكة".

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);
    }
}

في هذا المثال ، يمكن ملاحظة أن المراقب المالي يعرف الكثير عن "تقديم الطلب" ، كما أنه مكلف بمهمة إخطار المشتري وحجز البضائع.

سنسعى جاهدين للتأكد من أن وحدة التحكم يجب أن تتحكم فقط في العملية بالكامل وليس منطق الأعمال.

بادئ ذي بدء ، سوف نتحقق من صحة المعلمات في فئة طلب منفصلة.

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);
    }
}

أفضل بالفعل. تم نقل كل منطق الأعمال إلى الخدمة. وحدة التحكم تعرف فقط عن فئتي OrderRequest و OrderService - الحد الأدنى من مجموعة المعلومات اللازمة للقيام بعملها.

ولكن الآن خدمتنا تحتاج أيضا إلى إعادة هيكلة. دعونا نأخذ منطق إرسال الرسائل القصيرة إلى فصل منفصل.

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 ، وتحديد SmsRu من خلال موفر SmsServiceProvider .

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 . فئة ستقوم بتتبع الأحداث بسلوك معين لنموذج الطلب الخاص بنا وتعيين منطق منطق إخطار العميل لها.

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);
    }

لا تنس تسجيل OrderObserver في AppServiceProvider .

استنتاج


أفهم أنني أصف أشياء مبتذلة إلى حد ما في هذا المنشور ، لكنني أردت أن أبين كيف يتم تنفيذ هذا المبدأ في إطار Laravel.

مبدأ SPR هو واحد من أهم ويمهد الطريق للباقي. مطلوب للاستخدام ، خاصة وأن إطار Laravel يوفر جميع الأدوات اللازمة.

All Articles