概要

  • 要求処理するオブジェクトをチェーン状につなぐ。
  • 要求処理するまでチェーンに沿ってオブジェクトへ要求を渡していく。

を行うデザインパターン

責任のたらい回しとも言われており。
チェーン状に繋がれたお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
23
abstract 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;
}

次の判定が存在すれば、それを実行する様に実装します。

あとは、それぞれの判定処理を実装します。

PovertyJudger

1
2
3
4
5
6
7
class PovertyJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->isRich() === false;
}
}

VirtuousManJudger

1
2
3
4
5
6
7
class VirtuousManJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->getVirtuous() >= 20;
}
}

LuckyBoyJudger

1
2
3
4
5
6
7
class VirtuousManJudger extends WinningJudger
{
public function judge(Drawable $drawable) : bool
{
return $drawable->getFortune() >= mt_rand(1, 100);
}
}

この様に、それぞれの判定部分で要求処理する。
このとき、それぞれの判定部分は処理にのみ注力すればよい。

Drawable

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
class 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 Code

1
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 'ハズレです。';

このコードでは、次の順にチェーンを作成しています。

  1. virtuousManJudger -> luckyBoyJudger
  2. povertyJudger -> virtuousManJudger -> luckyBoyJudger

今回は決め打ちで作成していますが、nextJudgerメソッドをcallするだけなので、動的にも作成しやすく、
チェーンの追加も容易な構造になっています。

まとめ

  1. 要求処理するクラスのチェーンを作る
  2. チェーンに沿って要求を渡していく
  3. 必ず処理される訳ではない。

参考

Wikipeida
14.Chain of Responsibility パターン | TECHSCORE(テックスコア)