1402/02/17 6 دقیقه 1142 کلمه

الگوهای طراحی - نمونه هایی از فریم‌ورک لاراول

لاراول مفاهیم پایه

آیا می خواهید الگوهای طراحی را یاد بگیرید؟ یک راز جالب در مورد دیزاین پترن ها وجود دارد: همه شما زمان کار با لاراول از الگوهای طراحی به وفور استفاده می‌کنید. در این نوشته شما را با الگوهای طراحی Facade، Builder، Adapter، Singlton، Observer که در هسته فریم‌ورک لاراول استفاده شده اند آشنا خواهیم کرد.

design-patterns-examples-from-laravel-framework-core

در ادامه مطالب زیر ارائه خواهند شد :

الگوی طراحی Builder

Query Builder پرکاربردترین و آشنا ترین کلاسی است که در لاراول از الگوی طراحی Builder استفاده می‌کند. همچنین مدل‌های Eloquent از این کلاس توسعه یافته اند.

جهت آشنایی با این الگوی طراحی از مثال Query Builder استفاده می‌کنیم:

Query Builder

DB::table('users')
    ->where('name', 'Ali')
    ->where('age', '>', 22)
    ->orderBy('age')
    ->get();

در مثال بالا کلاس DB از الگوی Builder پیروی می کند. کوئری از متدهای زنجیره‌ای روی یک پایگاه داده ساخته می‌شود.

در زیر متدهای این زنجیره کد را بررسی می‌کنیم:

  • DB::table('users') - با جدول 'کاربران' کار می‌کند.
  • where('name', 'Ali')<- - فقط کاربران با نام 'Ali' باید انتخاب شوند.
  • where('age', '>', 22)<- - فقط کاربران با سن بالاتر از 22 سال باید انتخاب شوند.
  • orderBy('age') - کاربران را بر اساس سن مرتب می‌کند.
  • ()get<- - کوئری ساخته شده را اجرا می‌کند و نتایج را از پایگاه داده واکشی می‌کند.

هر یک از این متدها که زنجیره را تشکیل داده‌اند، به صورت مستقل به کوئری خام SQL اضافه می‌شوند. کدهای متناظر SQL هر متد در زیر آورده شده‌اند:

  • ...DB::table('users') - SELECT * FROM users
  • ...'where('name', 'Ali') - ...WHERE name = 'Ali<-
  • ...where('age', '>', 22) - ...WHERE name = 'Ali' AND age > 22<-
  • ...orderBy('age') - ...ORDER BY age<-
  • get() - SELECT * FROM users WHERE name = 'Ali' AND age > 22 ORDER BY age<-

همچنین کد زیر را برای ایجاد کوئری آپدیت (بروزرسانی) می توان مثال زد :

Query Builder

DB::table('users')
    ->where('name', 'Ali')
    ->update(['age' => 22]);

مثال بالا به شکل زیر توصیف می‌شود:

  • DB::table('users') - جدول 'کاربران' انتخاب می‌شود.
  • where('name', 'Ali')<- - کاربرانی با نام 'Ali' به روز خواهند شد.
  • update(['age' => 22])<- - ستون «سن» را برای همه کاربران انتخاب شده به عدد 22 تغییر می‌دهد.

کدهای متناظر SQL هر متد در زیر آورده شده‌اند:

  • ...DB::table('users') - UPDATE users
  • ...'where('name', 'Ali') - ...WHERE name = 'Ali<-
  • ...update(['age' => 22]) - ...SET age = 22<-
  • 'get() - UPDATE users SET age = 22 WHERE name = 'Ali<-

کوئری توسط متدهای زنجیره‌ای ساخته شده و در نهایت به صورت یکجا اجرا می‌شود. این الگوی طراحی Builder در عمل است!


الگوی طراحی Facade

Facade یکی دیگر از دیزاین پترن های رایج است. یک الگوی واقعا ساده است و این امکان را می‌دهد پیچیدگی سیستم را در پشت یک رابط ساده پنهان کنید.

به مثال Facade احراز هویت دقت کنید:

Auth Facade

Auth::login($user);
Auth::check();
Auth::user();
Auth::id();
Auth::logout();
Auth::attempt($credentials);

با توجه به اینکه کلاس Auth در برنامه‌های لاراولی بسیار پرکاربرد هست اکثر برنامه نویسان لاراول با آن آشنایی کافی دارند. البته خیلی از برنامه نویسان از متد کمکی این کلاس استفاده می‌کنند. در زیر لیستی از پرکاربردترین متدهای کمکی ()auth آورده شده است.

Auth helper

auth()->login($user);
auth()->check();
auth()->user();
auth()->id();
auth()->logout();
auth()->attempt($credentials);

در حقیقت تابع کمکی ()auth یک نمونه از کلاس Auth هست. با این وجود که سینتکس متفاوتی دارد. همچنین عملکرد هردو از نظر کارایی و سرعت یکسان هست. برای اینکه درک مناسبی از این کلاس داشته باشید بهتر است عمیق تر برویم و داخل کلاس را موشکافی کنیم.

درون «Auth»، یک کلاس Illuminate\Auth\AuthManager\ هست و احراز هویت را مدیریت می‌کند. این کلاس دارای متدهای زیادی است، اما نگران به یاد سپردن این متدها نباشید چون به کمک الگوی طراحی Facade متداول‌ترین متدهای این کلاس دردسترس قرار می‌گیرند. این الگوی طراحی Facade در عمل است!

پاداش - به کمک کلاس AuthManager می‌توانید مسیردهی‌های احراز هویت را ریجیستر کنید:

Illuminate/Auth/AuthManager

/**
 * Register the typical authentication routes for an application.
 *
 * @param  array  $options
 * @return void
 *
 * @throws \RuntimeException
 */
public static function routes(array $options = [])
{
    if (! static::$app->providerIsLoaded(UiServiceProvider::class)) {
        throw new RuntimeException('In order to use the Auth::routes() method, please install the laravel/ui package.');
    }
    static::$app->make('router')->auth($options);
}

البته به کمک همین کلاس کدهای سفارشی زیادی می‌توان نوشت.


الگوی طراحی Adapter

سیستم‌های مختلف با توجه به داشتن استانداردهای متفاوت عملکردهای متنوعی از خود نشان می‌دهند. مهم نیست که API شخص ثالث یا سرویس ایمیل باشد. چون دارای استانداردها و انتظار داده های متفاوتی هستند. در چنین شرایطی الگوی طراحی Adapter به داد برنامه نویسان می‌آید!

هدف از این دیزاین پترن تطبیق کدها با فرمت مشخص است. مثال بسیار ساده تر هنگامی است که می‌خواهیم از آداپتور موبایل برای تبدیل پریز به کابل USB استفاده کنیم. بسیاری از برنامه نویسان از این الگوی طراحی بارها استفاده می‌کنند در حالی که کاملا بفمند چگونه عمل می‌کند. یکی از بخش های لاراول که این دیزاین پترن در آن استفاده شده است، Notifications است:

لاراول به کمک اعلان‌ها، ورودی‌های شما را با استفاده از درایورهای مختلف مانند ایمیل، کانال‌های پخش، Slack و SMS و درایورهای سفارشی تطبیق می‌دهند.

معمولا از متد toMail داخل کلاس Notification استفاده می‌کنید:

Notification

public function toMail($notifiable)
{
    return (new MailMessage)
        ->line('The introduction to the notification.')
        ->action('Link button', route('link'));
}

در این مثال، داده‌ها به گونه‌ای تطبیق داده شده است تا با کلاس MailMessage که برای ارسال ایمیل استفاده می‌شود، سازگار شود. این الگوی طراحی Adapter در عمل است! دو لایه اصلی وجود دارد:

  1. داده‌ها را را برای سازگاری با کلاس MailMessage تغییر می‌دهیم.
  2. کلاس MailMessage داده‌ها را برای سازگاری با API ایمیل تغییر می‌دهد.

همه این فرایندها به صورت یکپارچه در پشت صحنه اتفاق می‌افتد و نیاز نیست نگران ساختار API مقصد باشیم. فقط کافی است داده‌ها را با کلاس MailMessage تطبیق دهیم و تمام!

اگر درایور Slack را نیاز داشته باشیم، با افزودن متد toSlack به کلاس Notification و سازگارکردن داده‌ها با کلاس SlackMessage می‌توانیم از آن درایور استفاده کنیم. به مثال زیر دقت کنید:

Notification

//
public function toSlack($notifiable)
{
    return (new SlackMessage)
        ->content('The introduction to the notification.');
}
  1. داده‌ها را برای سازگاری با کلاس SlackMessage تغییر می‌دهیم.
  2. کلاس SlackMessage داده‌ها را برای سازگاری با Slack API تغییر می‌دهد.

و با این الگوهای طراحی است که تجربه خوب توسعه را داریم 👌😊


الگوی طراحی Singlton

الگوی دیگری که بسیار پرکاربرد است و در هسته لاراول استفاده شده است، الگوی طراحی Singlton است. هدف اصلی این دیزاین پترن ساخت یک نمونه از کلاس خاص است که همه جای برنامه در دسترس ما باشد. ساخت یک نمونه و دسترسی از هربخش از برنامه این امکان را به ما می‌دهد تا از ساخت نمونه‌های تکراری جلوگیری شود.

در لاراول کلاس‌های زیادی هستند که از این الگو بهره می‌برند. یکی از کلاس‌های هسته لاراول App است که ابتدای اجرای برنامه یکبار ساخته می‌شود و تا زمانی که برنامه بسته نشده است در تمام بخش‌های برنامه دردسترس است. همچنین یکی از آشناترین کلاس‌ها که از این الگوی طراحی استفاده می‌کند کلاس درخواست است که هم به صورت کلاس Request  و هم به صورت تابع کمکی ()request دردسترس است.

برای اینکه بفهمیم کلاس Request و تابع کمکی آن هردو خروجی یکسانی دارند از مثال زیر استفاده کردیم:

Controller

public function index(Request $request)
{
    dump(request()->all());
    request()->merge(['test' => '123']);
    dump(request()->all());
    dd($request->all());
}

مثالی که زدیم موارد زیر را خط به خط اجرا می کند.

  1. متد ()all<- تمام مقادیر داخل درخواست را واکشی کرده است.
  2. متد ()merge<- کلید جدیدی به داده‌های درخواست اضافه می‌کند.
  3. دوباره واکشی از مقادیر داده های درخواست انجام می‌دهیم.
  4. این بار با استفاده از نمونه ساخته شده داده‌ها را واکشی می‌کنیم.

نتایج حاصل برای کلاس Request  و تابع کمکی ()request یکسان هستند.این الگوی طراحی Singlton در عمل است!

شما در پروژه خود از کلاس استفاده می‌کنید یا تابع کمکی ؟


الگوی طراحی Observer

هدف اصلی این دیزاین پترن ردیابی رویدادهای در حال رخ دادن هستند و کلاس‌های دیگر را از رویداد در حال انجام مطلع می‌کنند. این کار با کمک Observers انجام می‌شود که به رویدادهای مشخص در کلاس Models گوش می‌دهند:

app/Observers/TransactionObserver.php

// ...
class TransactionObserver
{
    public function created(Transaction $transaction): void
    {
        $transaction->client->notify(new \App\Notifications\TransactionCreated($transaction));
    }
    // ...
}

app/Models/Transaction.php

public function boot(): void
{
    self::observe(TransactionObserver::class);
}

در مثال بالا یک Observer ساختیم که رویداد created را در مدل Transaction ردیابی می‌کند و هنگامی که رویداد صدا زده می‌شود یک اعلان سفارشی به کاربرارسال می‌‎کند.

اگر ما نیاز به عملیات بیشتری (مانند ساخت فاکتور) باشیم فقط کافی است متد دیگری را به Observer اضافه کنیم!

app/Observers/TransactionObserver.php

public function created(Transaction $transaction)
{
    $transactionService = new TransactionService();
    $transactionService->generateInvoice($transaction);
    $transaction->client->notify(new \App\Notifications\TransactionCreated($transaction));
}

در این مثال با استفاده از TransactionObserver اگر رویداد created رخ دهد، هم برای کاربر فاکتور صادر می‌شود و هم اعلان ارسال می‌شود. این الگوی طراحی Observer در عمل است!


نتیجه گیری

الگوها در تمام ابزارهایی که ما در دسترس داریم مشترک هستند. در این نوشته نگاهی خیلی مختصر به آنچه که ممکن است به طور معمول در لاراول استفاده شود انداختیم، اما الگوهای بسیار بیشتری در لاراول هستند. با مطالعه این نوشته آشنایی خوبی در مورد دیزاین پترن‌ها بدست آورید! پیشنهاد می‌کنم از این الگوهای طراحی در پروژه‌های خود به صورت عملی استفاده کنید تا کدی تمیز، ساختاریافته و توسعه پذیر داشته باشید.


قدم بعدی

در مترووب صرفا به این نوشته کفایت نخواهد شد و آغازی برای درک کامل تر مفاهیم پایه و اساسی برنامه نویسی است. برنامه نویسی وب به ویژه لاراول هم مستثنی نیست و نیاز است تا همین مفاهیم را کامل درک کنید.

در آینده نزدیک درسنامه‌ای مفصل و همچنین دوره‌های ویدیویی کامل تری در خصوص الگوهای طراحی Design Patterns ارائه خواهد شد.


علی مهدوی

علی مهدوی برنامه نویس ارشد وب