Chain of Responsibility
Tweet概要
要求
を処理
するオブジェクトをチェーン状につなぐ。要求
を処理
するまでチェーンに沿ってオブジェクトへ要求を渡していく。
を行うデザインパターン
責任のたらい回しとも言われており。
チェーン状に繋がれたおbジェクトは、自分の責任範囲以外は処理しない。
利点
- チェーンを動的に変更しやすい。
処理
と要求
が緩い結合になる。(要求は必ず処理されるわけではない)処理
の追加が容易。- 複数の条件に一致するものがあるかを判定するときに使いやすい。
例
くじの抽選を行う。
抽選条件は、下記の2項目のうちどれかを満たさない場合はハズレ
- 抽選オブジェクトのisRichフラグがfalseである。
- 抽選オブジェクトのvirtuousプロパティが20以上ある。
- 抽選オブジェクトのfortuneプロパティが乱数(1〜100)を上回る。
UML
重要なところ
チェーン状につなぐ基底クラスの実装。
あとは、条件判定ごとにクラスを実装する。
ここが、要求
を処理
する部分となる。
PHPで書いてみる
WinningJudger(Chain of Responsibility)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23abstract class WinningJudger {
private $nextJudger;
public function setNextWinningConditions(WinningJudger $condition) : WinningJudger
{
$this->nextJudger = $condition;
return $this;
}
public function isWinning(Drawable $drawable) : bool
{
$result = $this->judge($drawable);
if (!$result) {
return false;
} elseif (isset($this->nextJudger)) { // チェーンに沿って要求を渡す
return $this->nextJudger->isWinning($drawable);
}
return true; // 何も処理されなかった場合はtrueを返す
}
abstract public function judge(Drawable $drawable) : bool;
}
次の判定が存在すれば、それを実行する様に実装します。
あとは、それぞれの判定処理を実装します。
PovertyJudger1
2
3
4
5
6
7class PovertyJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->isRich() === false;
}
}
VirtuousManJudger1
2
3
4
5
6
7class VirtuousManJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->getVirtuous() >= 20;
}
}
LuckyBoyJudger1
2
3
4
5
6
7class VirtuousManJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->getFortune() >= mt_rand(1, 100);
}
}
この様に、それぞれの判定部分で要求
を処理
する。
このとき、それぞれの判定部分は処理
にのみ注力すればよい。
Drawable1
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
28class Drawable
{
private $fortune;
private $virtuous;
private $rich;
public function __construct(int $fortune, int $virtuous, bool $rich)
{
$this->fortune = $fortune;
$this->virtuous = $virtuous;
$this->rich = $rich;
}
public function isRich() : bool
{
return (bool)$this->rich;
}
public function getFortune() : int
{
return (int)$this->fortune;
}
public function getVirtuous() : int
{
return (int)$this->virtuous;
}
}
Client Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$povertyJudger = new PovertyJudger();
$virtuousManJudger = new VirtuousManJudger();
$luckyBoyJudger = new LuckyBoyJudger();
// チェーンを作成
$virtuousManJudger->nextJudger($luckyBoyJudger);
$povertyJudger->nextJudger($virtuousManJudger);
$drawable = new Drawable(101, 530000, true);
if ($povertyJudger->isWinning($drawable)) {
echo 'あたりです。';
return;
}
echo 'ハズレです。';
このコードでは、次の順にチェーンを作成しています。
- virtuousManJudger -> luckyBoyJudger
- povertyJudger -> virtuousManJudger -> luckyBoyJudger
今回は決め打ちで作成していますが、nextJudgerメソッドをcallするだけなので、動的にも作成しやすく、
チェーンの追加も容易な構造になっています。
まとめ
要求
を処理
するクラスのチェーンを作る- チェーンに沿って
要求
を渡していく - 必ず
処理
される訳ではない。
参考
Wikipeida
14.Chain of Responsibility パターン | TECHSCORE(テックスコア)