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を利用して新しい割引種別を追加するだけで割引金額を求めることができる。
まとめ
要求
と要求の受付
と要求の対応
を分離する。要求の受付
は要求
の抽象に依存し、要求
は要求の対応
に依存する。