Command
Tweet概要
コマンド(要求)をオブジェクトとしてカプセル化し、要求と要求の受付と要求の対応を分離してするパターン。
利点
要求と要求の受付と要求の対応を別々で考えて実装できるので、要求が追加されるパターンに強い。要求の対応が独立していて、再利用性が高い。
例
料金計算の割引コマンドを実装する
UML
重要なところ
要求を実装する。
この時、要求は、抽象クラスと具象クラスで実装する。
要求の受付を実装する。
実装するコマンドは要求の抽象クラスへ依存させる。
PHPで書いてみる
要求の抽象クラス1
2
3
4
5
6
7
8
9
10abstract class DiscountCommand {
protected $price;
public function __construct(Price $price)
{
$this->price = $price;
}
abstract public function discount() : void;
}
要求の具象クラス1
2
3
4
5
6class HalfDiscountCommand extends DiscountCommand {
public function discount() : void
{
$this->price->discount(round($this->price->get() / 2));
}
}
1 | class TaxDiscountCommand extends DiscountCommand { |
要求の受付の実装1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class DiscountCommandInvoker {
private $commands = [];
public function setCommand(DiscountCommand $command) // 抽象に依存させる
{
$this->commands[] = $command;
}
public function run()
{
foreach ($this->commands as $command)
{
$command->discount();
}
}
}
要求の対応の実装1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Price {
private $price;
public function __construct(int $price) // 抽象に依存させる
{
$this->price = $price;
}
public function get() : int
{
return $this->price;
}
public function getTax() : int
{
return $this->price - floor($this->price / 1.08);
}
public function discount(int $price)
{
$this->price = $this->price - $price;
}
}
Client Code1
2
3
4
5
6
7$price = new Price(216);
$invoker = new DiscountCommandInvoker();
$invoker->setCommand(new TaxDiscountCommand($price));
$invoker->setCommand(new HalfDiscountCommand($price));
$invoker->run();
echo $price->get() . '円';
これで、完全に要求と要求の受付と要求の対応が分離された。
また、要求の対応が独立していて再利用が非常にしやすい。
割引種別が増えた場合でも、setCommandを利用して新しい割引種別を追加するだけで割引金額を求めることができる。
まとめ
要求と要求の受付と要求の対応を分離する。要求の受付は要求の抽象に依存し、要求は要求の対応に依存する。