3.1. Chain of Responsibility¶
3.1.1. تعریف¶
در الگوی طراحی Chain of Responsibility ما ساختاری رو تعریف می کنیم که در اون چندین آبجکت مسئول پاسخ گویی و پردازش یک درخواست میشن.
زمانی که یک درخواست ایجاد میشه در زنجیره یا Chain حرکت می کنه تا زمانی که یک آبجکت پیدا بشه که بتونه درخواست رو پاسخ بده.
3.1.2. اجزاء¶
الگوی طراحی Chain of Responsibility از چند بخش اصلی تشکیل میشه:
Concrete Handlers: پیاده سازی هندل کننده های مورد نظر
Adaptee: کلاسی که می بایست آداپته شود.
Client
Olimpiu.pop, CC BY-SA 3.0, via Wikimedia Commons
3.1.3. چه زمانی استفاده میشه؟¶
این الگو زمانی در برنامه استفاده میشه که چندین آبجکت امکان هندل کردن یک درخواست رو داشته باشن اما دقیقا مشخص نباشه که کدوم مسئول این کار هست.
زمانی رو تصویر کنید که یک فرد با قسمت پشتیبانی یک مرکز تماس میگیره و نفر اول نمیتونه مشکلش رو حل کنه و ارجاع میده به نفر بعد و این فرآیند تا زمانی ادامه پیدا می کنه که مشکل حل بشه.
در حالت دیگه به این صورت استفاده میشه که هر کدوم به نحوی تاثیری روی درخواست میگذارن و اون رو به نفر بعد میدن.
Caution
✅ مزایای استفاده
*. امکان مدیریت روی ترتیب request handling
*. رعایت اصل Open/Closed از اصول Solid: اضافه شدن handler های جدید باعث تغییر ساختار کد نمیشه
*. رعایت اصلی تک مسئولیتی از اصول SOLID با جداسازی کلاس عملیاتی از منطق برنامه
Warning
❌ معایب استفاده
این امکان وجود داره که بعضی از درخواست ها از کل زنجیره عبور کنن بدون اینکه handle بشن
3.1.4. کاربرد عملی¶
مطرح ترین و شناخته شده ترین مثالی که از الگوی طراحی Chain of Responsibility شناخته میشه، HTTP request middleware هست.
پیاده سازی این ساختار رو میشه در اکثر فریمورک های مطرح PHP هم دید.
ساختار کلی این الگو در فریمورک لاراول رو با هم بررسی می کنیم.
3.1.5. پیاده سازی¶
خب هر Middleware باید چنین الگویی داشته باشه:
1<?php
2
3// Middleware interface
4interface Middleware {
5 public function handle($request, Closure $next);
6}
و پیاده سازی ها در لاراول به این شکل هست:
1<?php
2
3// Concrete middleware
4class LoggingMiddleware implements Middleware
5{
6 public function handle($request, Closure $next)
7 {
8 echo "Logging middleware handled the request.\n";
9 return $next($request);
10 }
11}
12
13class AuthorizationMiddleware implements Middleware
14{
15 public function handle($request, Closure $next)
16 {
17 echo "Authorization middleware handled the request.\n";
18 return $next($request);
19 }
20}
21
22class ThrottlingMiddleware implements Middleware
23{
24 public function handle($request, Closure $next)
25 {
26 echo "Throttling middleware handled the request.\n";
27 return $next($request);
28 }
29}
هر Middleware درخواستی که دریافت کرده را پردازش و درخواست را به Middleware بعد منتقل می کند.
این زنجیره یا chain میان Middleware ها از طریق Kernel ایجاد می شود:
1<?php
2
3// Middleware kernel
4class Kernel
5{
6 protected array $middlewares = [
7 LoggingMiddleware::class,
8 AuthorizationMiddleware::class,
9 ThrottlingMiddleware::class,
10 ];
11
12 public function handle($request)
13 {
14 $pipeline = array_reduce(array_reverse($this->middlewares), function ($next, $middleware) {
15 return function ($request) use ($next, $middleware) {
16 return (new $middleware)->handle($request, $next);
17 };
18 }, function ($request) {
19 return response('Not Found', 404); // Laravel response
20 });
21
22 return $pipeline($request);
23 }
24}
در اینجا متد handle از متد array_reduce استفاده می کنه که به صورت ترتیب برعکس روی آرایه Middleware ها حرکت می کنه و به ترتیب نتیجه رو از یک Middleware به دیگری میده تا در نهایت request نهایی برگرده.
3.1.6. نحوه فراخوانی¶
1<?php
2
3// Client code
4$kernel = new Kernel();
5$response = $kernel->handle(request());
6
7$response->send();
درخواست به متد handle داده می شود تا زنجیره ی Middleware ها به ترتیب درخواست را handle کنند.
3.1.7. مثال دوم¶
یا مثال دیگه میتونه حالتی باشه که بحث احراز هویت کاربر رو داریم توسط Middleware ها بررسی می کنیم.
در سطح اول زنجیره، اطلاعات ورود کاربر رو بررسی می کنیم و در سطح دوم سطح دسترسی کاربر به بخش مورد نظر و…