Command パターン | デザインパターン
2022年9年23日
デザインパターン
Command
PHP
オライリージャパンによる Head First シリーズ デザインパターン(第2版)第6章 Command パターンを、 PHPで書き直してみようという試みです。
Light.php
namespace Command;
class Light {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function on() : void {
echo $this->name . ' 照明が点いています';
echo "\n";
}
public function off() : void {
echo $this->name . ' 照明が消えています';
echo "\n";
}
}
CeilingFan.php
namespace Command;
class CeilingFan {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function on() : void {
echo $this->name . ' シーリングファンの強さは「強」です';
echo "\n";
}
public function off() : void {
echo $this->name . ' シーリングファンは止まっています';
echo "\n";
}
}
Stereo.php
namespace Command;
class Stereo {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function on() : void {
echo $this->name . ' オーディオの電源が入っています';
echo "\n";
}
public function off() : void {
echo $this->name . ' オーディオの電源は切れています';
echo "\n";
}
public function setCD() : void {
echo $this->name . ' オーディオがCD入力に設定されています';
echo "\n";
}
public function setVolume(int $volume) : void {
echo $this->name . ' オーディオのボリュームが' . strval($volume) . 'に設定されています';
echo "\n";
}
}
Command.php
namespace Command;
interface Command
{
public function execute() : void;
}
NoCommand.php
namespace Command;
class NoCommand implements Command {
public function execute() : void {
echo 'NoCommand';
echo "\n";
}
public function undo() : void {
echo 'NoCommand';
echo "\n";
}
}
LightOnCommand.php
namespace Command;
class LightOnCommand implements Command {
public Light $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() : void {
$this->light->on();
}
}
LightOffCommand.php
namespace Command;
class LightOffCommand implements Command {
public Light $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() : void {
$this->light->off();
}
}
CeilingFanOnCommand.php
namespace Command;
class CeilingFanOnCommand implements Command {
public CeilingFan $ceilingFan;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->ceilingFan->on();
}
}
CeilingFanOffCommand.php
namespace Command;
class CeilingFanOffCommand implements Command {
public CeilingFan $ceilingFan;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->ceilingFan->off();
}
}
StereoOnWithCDCommand.php
namespace Command;
class StereoOnWithCDCommand implements Command {
public Stereo $stereo;
public function __construct(Stereo $stereo) {
$this->stereo = $stereo;
}
public function execute() : void {
$this->stereo->on();
$this->stereo->setCD();
$this->stereo->setVolume(11);
}
}
StereoOffCommand.php
namespace Command;
class StereoOffCommand implements Command {
public Stereo $stereo;
public function __construct(Stereo $stereo) {
$this->stereo = $stereo;
}
public function execute() : void {
$this->stereo->off();
}
}
StereoOffCommand.php
namespace Command;
class RemoteControl {
public array $onCommand;
public array $offCommand;
public function __construct(){
$this->onCommand = [];
$this->offCommand = [];
$noCommand = New NoCommand();
$this->onCommand = array_pad($this->onCommand, 7, $noCommand);
$this->offCommand = array_pad($this->offCommand, 7, $noCommand);
}
public function setCommand(int $slot, Command $onCommand, Command $offCommand) : void {
$this->onCommand[$slot] = $onCommand;
$this->offCommand[$slot] = $offCommand;
}
public function onButtonWasPushed(int $slot) : void {
$this->onCommand[$slot]->execute();
}
public function offButtonWasPushed(int $slot) : void {
$this->offCommand[$slot]->execute();
}
public function toString() : string {
$stringBuf = null;
for ($i = 0; $i < count($this->onCommand); $i++) {
$onCommandClassName = str_replace(__NAMESPACE__ . '\\', '', get_class($this->onCommand[$i]));
$offCommandClassName = str_replace(__NAMESPACE__ . '\\', '', get_class($this->offCommand[$i]));
$stringBuf .= '[スロット' . $i . ']' . ' ' . $onCommandClassName . ' ' . $offCommandClassName . "\n";
}
return $stringBuf;
}
}
index.php
use Command\Light;
use Command\LightOnCommand;
use Command\LightOffCommand;
use Command\CeilingFan;
use Command\CeilingFanOnCommand;
use Command\CeilingFanOffCommand;
use Command\Stereo;
use Command\StereoOnWithCDCommand;
use Command\StereoOffCommand;
use Command\RemoteControl;
$livingRoomLight = new Light('リビングルーム');
$livingRoomLightOn = new LightOnCommand($livingRoomLight);
$livingRoomLightOff = new LightOffCommand($livingRoomLight);
$kitchenLight = new Light('キッチン');
$kitchenLightOn = new LightOnCommand($kitchenLight);
$kitchenLightOff = new LightOffCommand($kitchenLight);
$ceilingFan = new CeilingFan('リビングルーム');
$ceilingFanOn = new CeilingFanOnCommand($ceilingFan);
$ceilingFanOff = new CeilingFanOffCommand($ceilingFan);
$stereo = new Stereo('リビングルーム');
$stereoOn = new StereoOnWithCDCommand($stereo);
$stereoOff = new StereoOffCommand($stereo);
$remoteControl = new RemoteControl();
$remoteControl->setCommand(0, $livingRoomLightOn, $livingRoomLightOff);
$remoteControl->setCommand(1, $kitchenLightOn, $kitchenLightOff);
$remoteControl->setCommand(2, $ceilingFanOn, $ceilingFanOff);
$remoteControl->setCommand(3, $stereoOn, $stereoOff);
echo $remoteControl->toString();
$remoteControl->onButtonWasPushed(0);
$remoteControl->offButtonWasPushed(0);
$remoteControl->onButtonWasPushed(1);
$remoteControl->offButtonWasPushed(1);
$remoteControl->onButtonWasPushed(2);
$remoteControl->offButtonWasPushed(2);
$remoteControl->onButtonWasPushed(3);
$remoteControl->offButtonWasPushed(3);
出力結果
[スロット0] LightOnCommand LightOffCommand
[スロット1] LightOnCommand LightOffCommand
[スロット2] CeilingFanOnCommand CeilingFanOffCommand
[スロット3] StereoOnWithCDCommand StereoOffCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
リビングルーム 照明が点いています
リビングルーム 照明が消えています
キッチン 照明が点いています
キッチン 照明が消えています
リビングルーム シーリングファンの強さは「強」です
リビングルーム シーリングファンは止まっています
リビングルーム オーディオの電源が入っています
リビングルーム オーディオがCD入力に設定されています
リビングルーム オーディオのボリュームが11に設定されています
リビングルーム オーディオの電源は切れています
LightOnCommand.php
namespace Command;
class LightOnCommand implements Command {
public Light $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() : void {
$this->light->on();
}
public function undo() : void {
$this->light->off();
}
}
LightOffCommand.php
namespace Command;
class LightOffCommand implements Command {
public Light $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() : void {
$this->light->off();
}
public function undo() : void {
$this->light->on();
}
}
RemoteControlWithUndo.php
namespace Command;
class RemoteControlWithUndo {
public array $onCommand;
public array $offCommand;
public $undoCommand;
public function __construct(){
$this->onCommand = [];
$this->offCommand = [];
$noCommand = New NoCommand();
$this->onCommand = array_pad($this->onCommand, 7, $noCommand);
$this->offCommand = array_pad($this->offCommand, 7, $noCommand);
$this->undoCommand = $noCommand;
}
public function setCommand(int $slot, Command $onCommand, Command $offCommand) : void {
$this->onCommand[$slot] = $onCommand;
$this->offCommand[$slot] = $offCommand;
}
public function onButtonWasPushed(int $slot) : void {
$this->onCommand[$slot]->execute();
$this->undoCommand = $this->onCommand[$slot];
}
public function offButtonWasPushed(int $slot) : void {
$this->offCommand[$slot]->execute();
$this->undoCommand = $this->offCommand[$slot];
}
public function undoButtonWasPushed(int $slot) : void {
$this->undoCommand->undo();
}
public function toString() : string {
$stringBuf = null;
for ($i = 0; $i < count($this->onCommand); $i++) {
$onCommandClassName = str_replace(__NAMESPACE__ . '\\', '', get_class($this->onCommand[$i]));
$offCommandClassName = str_replace(__NAMESPACE__ . '\\', '', get_class($this->offCommand[$i]));
$stringBuf .= '[スロット' . $i . ']' . ' ' . $onCommandClassName . ' ' . $offCommandClassName . "\n";
}
$undoCommand = str_replace(__NAMESPACE__ . '\\', '', get_class($this->undoCommand));
$stringBuf .= '[アンドゥ]' . ' ' . $undoCommand . "\n";
return $stringBuf;
}
}
index.php
use Command\Light;
use Command\LightOnCommand;
use Command\LightOffCommand;
use Command\RemoteControlWithUndo;
$livingRoomLight = new Light('リビングルーム');
$livingRoomLightOn = new LightOnCommand($livingRoomLight);
$livingRoomLightOff = new LightOffCommand($livingRoomLight);
$remoteControl = new RemoteControlWithUndo();
$remoteControl->setCommand(0, $livingRoomLightOn, $livingRoomLightOff);
$remoteControl->onButtonWasPushed(0);
$remoteControl->offButtonWasPushed(0);
echo $remoteControl->toString();
$remoteControl->undoButtonWasPushed(0);
$remoteControl->offButtonWasPushed(0);
$remoteControl->onButtonWasPushed(0);
echo $remoteControl->toString();
$remoteControl->undoButtonWasPushed(0);
出力結果
リビングルーム 照明が点いています
リビングルーム 照明が消えています
[スロット0] LightOnCommand LightOffCommand
[スロット1] LightOnCommand LightOffCommand
[スロット2] CeilingFanOnCommand CeilingFanOffCommand
[スロット3] StereoOnWithCDCommand StereoOffCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
[アンドゥ] LightOffCommand
リビングルーム 照明が点いています
リビングルーム 照明が消えています
リビングルーム 照明が点いています
[スロット0] LightOnCommand LightOffCommand
[スロット1] LightOnCommand LightOffCommand
[スロット2] CeilingFanOnCommand CeilingFanOffCommand
[スロット3] StereoOnWithCDCommand StereoOffCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
[アンドゥ] LightOnCommand
リビングルーム 照明が消えています
CeilingFan.php
namespace Command;
class CeilingFan {
public int $HIGH = 3;
public int $MEDIUM = 2;
public int $LOW = 1;
public int $OFF = 0;
public string $name;
public int $speed = 0;
public function __construct(string $name) {
$this->name = $name;
}
public function on() : void {
$this->speed = $this->HIGH;
echo $this->name . 'シーリングファンの強さは「強」です';
echo "\n";
}
public function high() : void {
$this->speed = $this->HIGH;
echo $this->name . 'シーリングファンの強さは「強」です';
echo "\n";
}
public function medium() : void {
$this->speed = $this->MEDIUM;
echo $this->name . 'シーリングファンの強さは「中」です';
echo "\n";
}
public function low() : void {
$this->speed = $this->LOW;
echo $this->name . 'シーリングファンの強さは「弱」です';
echo "\n";
}
public function off() : void {
$this->speed = $this->OFF;
echo $this->name . 'シーリングファンが止まっています';
echo "\n";
}
public function getSpeed() : int {
return $this->speed;
}
}
CeilingFanHighCommand.php
namespace Command;
class CeilingFanHighCommand implements Command {
public CeilingFan $ceilingFan;
public int $prevSpeed;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->prevSpeed = $this->ceilingFan->getSpeed();
$this->ceilingFan->high();
}
public function undo() : void {
if ($this->prevSpeed == $this->ceilingFan->HIGH) {
$this->ceilingFan->high();
} else if ($this->prevSpeed == $this->ceilingFan->MEDIUM) {
$this->ceilingFan->medium();
} else if ($this->prevSpeed == $this->ceilingFan->LOW) {
$this->ceilingFan->low();
} else if ($this->prevSpeed == $this->ceilingFan->OFF) {
$this->ceilingFan->high();
}
}
}
CeilingFanMediumCommand.php
namespace Command;
class CeilingFanMediumCommand implements Command {
public CeilingFan $ceilingFan;
public int $prevSpeed;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->prevSpeed = $this->ceilingFan->getSpeed();
$this->ceilingFan->medium();
}
public function undo() : void {
if ($this->prevSpeed == $this->ceilingFan->HIGH) {
$this->ceilingFan->high();
} else if ($this->prevSpeed == $this->ceilingFan->MEDIUM) {
$this->ceilingFan->medium();
} else if ($this->prevSpeed == $this->ceilingFan->LOW) {
$this->ceilingFan->low();
} else if ($this->prevSpeed == $this->ceilingFan->OFF) {
$this->ceilingFan->high();
}
}
}
CeilingFanLowCommand.php
namespace Command;
class CeilingFanLowCommand implements Command {
public CeilingFan $ceilingFan;
public int $prevSpeed;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->prevSpeed = $this->ceilingFan->getSpeed();
$this->ceilingFan->low();
}
public function undo() : void {
if ($this->prevSpeed == $this->ceilingFan->HIGH) {
$this->ceilingFan->high();
} else if ($this->prevSpeed == $this->ceilingFan->MEDIUM) {
$this->ceilingFan->medium();
} else if ($this->prevSpeed == $this->ceilingFan->LOW) {
$this->ceilingFan->low();
} else if ($this->prevSpeed == $this->ceilingFan->OFF) {
$this->ceilingFan->high();
}
}
}
CeilingFanOffCommand.php
namespace Command;
class CeilingFanOffCommand implements Command {
public CeilingFan $ceilingFan;
public int $prevSpeed;
public function __construct(CeilingFan $ceilingFan) {
$this->ceilingFan = $ceilingFan;
}
public function execute() : void {
$this->prevSpeed = $this->ceilingFan->getSpeed();
$this->ceilingFan->off();
}
public function undo() : void {
if ($this->prevSpeed == $this->ceilingFan->HIGH) {
$this->ceilingFan->high();
} else if ($this->prevSpeed == $this->ceilingFan->MEDIUM) {
$this->ceilingFan->medium();
} else if ($this->prevSpeed == $this->ceilingFan->LOW) {
$this->ceilingFan->low();
} else if ($this->prevSpeed == $this->ceilingFan->OFF) {
$this->ceilingFan->high();
}
}
}
index.php
use Command\CeilingFan;
use Command\CeilingFanHighCommand;
use Command\CeilingFanMediumCommand;
use Command\CeilingFanLowCommand;
use Command\CeilingFanOffCommand;
use Command\RemoteControlWithUndo;
$ceilingFan = new CeilingFan('リビングルーム');
$ceilingFanHigh = new CeilingFanHighCommand($ceilingFan);
$ceilingFanMedium = new CeilingFanMediumCommand($ceilingFan);
$ceilingFanOff = new CeilingFanOffCommand($ceilingFan);
$remoteControl = new RemoteControlWithUndo();
$remoteControl->setCommand(0, $ceilingFanMedium, $ceilingFanOff);
$remoteControl->setCommand(1, $ceilingFanHigh, $ceilingFanOff);
$remoteControl->onButtonWasPushed(0);
$remoteControl->offButtonWasPushed(0);
echo $remoteControl->toString();
$remoteControl->undoButtonWasPushed(0);
$remoteControl->onButtonWasPushed(1);
echo $remoteControl->toString();
$remoteControl->undoButtonWasPushed(1);
出力結果
リビングルームシーリングファンの強さは「中」です
リビングルームシーリングファンが止まっています
[スロット0] CeilingFanMediumCommand CeilingFanOffCommand
[スロット1] CeilingFanHighCommand CeilingFanOffCommand
[スロット2] NoCommand NoCommand
[スロット3] NoCommand NoCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
[アンドゥ] CeilingFanOffCommand
リビングルームシーリングファンの強さは「中」です
リビングルームシーリングファンの強さは「強」です
[スロット0] CeilingFanMediumCommand CeilingFanOffCommand
[スロット1] CeilingFanHighCommand CeilingFanOffCommand
[スロット2] NoCommand NoCommand
[スロット3] NoCommand NoCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
[アンドゥ] CeilingFanHighCommand
リビングルームシーリングファンの強さは「中」です
MacroCommand.php
namespace Command;
class MacroCommand implements Command {
public array $commands = [];
public function __construct(array $commands) {
$this->commands = $commands;
}
public function execute() : void {
for($i = 0; $i < count($this->commands); $i++){
$this->commands[$i]->execute();
}
}
public function undo() : void {
}
}
TV.php
namespace Command;
class TV {
public string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function on() : void {
echo $this->name . ' TVの電源が入っています';
echo "\n";
}
public function off() : void {
echo $this->name . ' TVの電源が切れています';
echo "\n";
}
public function setDVD() : void {
echo $this->name . ' TVのチャンネルがDVDに設定されています';
echo "\n";
}
}
TVOnCommand.php
namespace Command;
class TVOnCommand implements Command {
public TV $tv;
public function __construct(TV $tv) {
$this->tv = $tv;
}
public function execute() : void {
$this->tv->on();
$this->tv->setDVD();
}
public function undo() : void {
$this->tv->off();
}
}
TVOffCommand.php
namespace Command;
class TVOffCommand implements Command {
public TV $tv;
public function __construct(TV $tv) {
$this->tv = $tv;
}
public function execute() : void {
$this->tv->off();
}
public function undo() : void {
$this->tv->on();
}
}
Hottub.php
namespace Command;
class Hottub {
public function __construct() {
}
public function on() : void {
echo '風呂が40度まで熱くなっています';
echo "\n";
echo 'バブルバスが動いています!';
echo "\n";
}
public function off() : void {
echo '風呂が37度まで冷めています';
echo "\n";
}
}
HottubOnCommand.php
namespace Command;
class HottubOnCommand implements Command {
public Hottub $hottub;
public function __construct(Hottub $hottub) {
$this->hottub = $hottub;
}
public function execute() : void {
$this->hottub->on();
}
public function undo() : void {
$this->hottub->off();
}
}
HottubOffCommand.php
namespace Command;
class HottubOffCommand implements Command {
public Hottub $hottub;
public function __construct(Hottub $hottub) {
$this->hottub = $hottub;
}
public function execute() : void {
$this->hottub->off();
}
public function undo() : void {
$this->hottub->on();
}
}
HottubOffCommand.php
$light = new Light('リビングルーム');
$stereo = new Stereo('リビングルーム');
$tv = new TV('リビングルーム');
$hottub = new Hottub();
$lightOn = new LightOnCommand($light);
$stereoOn = new StereoOnCommand($stereo);
$tvOn = new TVOnCommand($tv);
$hottubOn = new HottubOnCommand($hottub);
$lightOff = new LightOffCommand($light);
$stereoOff = new StereoOffCommand($stereo);
$tvOff = new TVOffCommand($tv);
$hottubOff = new HottubOffCommand($hottub);
$partyOn = [$lightOn,$stereoOn,$tvOn,$hottubOn];
$partyOff = [$lightOff,$stereoOff,$tvOff,$hottubOff];
$partyOnMacro = new MacroCommand($partyOn);
$partyOffMacro = new MacroCommand($partyOff);
$remoteControl = new RemoteControlWithUndo();
$remoteControl->setCommand(0, $partyOnMacro, $partyOffMacro);
echo $remoteControl->toString();
echo '--- マクロのOnを押す ---' . "\n";
$remoteControl->onButtonWasPushed(0);
echo '--- マクロのOffを押す ---' . "\n";
$remoteControl->offButtonWasPushed(0);
出力結果
[スロット0] MacroCommand MacroCommand
[スロット1] NoCommand NoCommand
[スロット2] NoCommand NoCommand
[スロット3] NoCommand NoCommand
[スロット4] NoCommand NoCommand
[スロット5] NoCommand NoCommand
[スロット6] NoCommand NoCommand
[アンドゥ] NoCommand
--- マクロのOnを押す ---
リビングルーム 照明が点いています
リビングルーム オーディオの電源が入っています
リビングルーム TVの電源が入っています
リビングルーム TVのチャンネルがDVDに設定されています
風呂が40度まで熱くなっています
バブルバスが動いています!
--- マクロのOffを押す ---
リビングルーム 照明が消えています
リビングルーム オーディオの電源は切れています
リビングルーム TVの電源が切れています
風呂が37度まで冷めています