آیا می خواهید الگوهای طراحی را یاد بگیرید؟ یک راز جالب در مورد دیزاین پترن ها وجود دارد: همه شما زمان کار با لاراول از الگوهای طراحی به وفور استفاده میکنید. در این نوشته شما را با الگوهای طراحی Facade، Builder، Adapter، Singlton، Observer که در هسته فریمورک لاراول استفاده شده اند آشنا خواهیم کرد.
در ادامه مطالب زیر ارائه خواهند شد :
- الگوی طراحی Builder
- الگوی طراحی Facade
- الگوی طراحی Adapter
- الگوی طراحی Singlton
- الگوی طراحی Observer
- نتیجه گیری
- قدم بعدی
الگوی طراحی 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 در عمل است!
دو لایه اصلی وجود دارد:
- دادهها را را برای سازگاری با کلاس
MailMessage
تغییر میدهیم. - کلاس
MailMessage
دادهها را برای سازگاری با API ایمیل تغییر میدهد.
همه این فرایندها به صورت یکپارچه در پشت صحنه اتفاق میافتد و نیاز نیست نگران ساختار API مقصد باشیم. فقط کافی است دادهها را با کلاس MailMessage
تطبیق دهیم و تمام!
اگر درایور Slack را نیاز داشته باشیم، با افزودن متد toSlack
به کلاس Notification و سازگارکردن دادهها با کلاس SlackMessage
میتوانیم از آن درایور استفاده کنیم. به مثال زیر دقت کنید:
Notification
//
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('The introduction to the notification.');
}
- دادهها را برای سازگاری با کلاس
SlackMessage
تغییر میدهیم. - کلاس
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());
}
مثالی که زدیم موارد زیر را خط به خط اجرا می کند.
- متد
()all<-
تمام مقادیر داخل درخواست را واکشی کرده است. - متد
()merge<-
کلید جدیدی به دادههای درخواست اضافه میکند. - دوباره واکشی از مقادیر داده های درخواست انجام میدهیم.
- این بار با استفاده از نمونه ساخته شده دادهها را واکشی میکنیم.
نتایج حاصل برای کلاس 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 ارائه خواهد شد.
علی مهدوی برنامه نویس ارشد وب