Pattern: 職責鏈模式
Class Diagram: 新產品開發
情境:公司開發了一個新產品,客戶端有許多不同的請求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
class Request {
protected $type;
protected $content;
public function __construct(string $type, string $content) { $this->type = $type; $this->content = $content; }
public function getType() { return $this->type; }
public function getContent() { return $this->content; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
use Tests\Unit\ChainOfResponsibilityPattern\Software\Request;
class Program {
public function handle(Request $request) { $type = $request->getType(); $content = $request->getContent();
switch ($type) { case 'bug': return "Support已開始處理[$type:$content]的問題。"; break;
case 'feature': return "PM已開始處理[$type:$content]的問題。"; break;
default: return "Boss已開始處理[$type:$content]的問題。"; break; } } }
|
根據請求類型的不同,我們會交由不同的角色來處理問題。
但這些請求,可以透過區分請求類別的方式,
統一先交由Support處理,若Support無法處理,再轉給下個負責人。
以這樣的想法,讓我們用職責鏈模式改造它。
- 首先是抽象的處理器類別,當該處理器無法處理時,會轉給下一個處理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?php
namespace App\ChainOfResponsibilityPattern\Software\Abstracts;
use App\ChainOfResponsibilityPattern\Software\Request;
abstract class Handler {
protected $role;
protected $canHandleType = [];
protected $requestType;
protected $requestContent;
protected $nextHandler;
public function handle(Request $request): string { $this->requestType = $request->getType(); $this->requestContent = $request->getContent();
if ($this->canHandle()) { $role = $this->role; $result = "$role can solve [$this->requestType:$this->requestContent] issue."; return $result; }
return $this->nextHandler->handle($request); }
public function setNextHandler(Handler $handler) { $this->nextHandler = $handler; }
protected function canHandle() { return in_array($this->requestType, $this->canHandleType); } }
|
由canHandle()方法來知道,該處理器能不能處理。
由setNextHandler()方法,來決定下一個處理器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;
class Support extends Handler {
protected $role = 'Support';
protected $canHandleType = ['bug']; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;
class ProjectManager extends Handler {
protected $role = 'PM';
protected $canHandleType = ['bug', 'feature']; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;
class Boss extends Handler {
protected $role = 'Boss';
protected function canHandle() { return true; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
namespace App\ChainOfResponsibilityPattern\Software;
use App\ChainOfResponsibilityPattern\Software\Request; use App\ChainOfResponsibilityPattern\Software\Support;
class Program { public function handle(Request $request) { $support = new Support(); $projectManager = new ProjectManager(); $boss = new Boss();
$support->setNextHandler($projectManager); $projectManager->setNextHandler($boss);
return $support->handle($request); } }
|
這邊可以留意,若有未定義類型的請求,最終都會交由Boss處理器來捕捉。
[單一職責原則]
我們把處理器與處理器的順序視作兩種不同的職責。
[開放封閉原則]
無論是新增/修改處理器的邏輯,或者修改處理器的順序,
皆不會改動到所有程式碼。
[依賴反轉原則]
抽象的處理器類別依賴於自身(組合模式)。
實體的處理器類別則實現它。
ʕ •ᴥ•ʔ:一個很生活化的設計模式。