コマンド パターン

Command Pattern

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度まで冷めています
      

だてめがね
...
©️ ponpocopy

とある企業で社内SEをしています。 自身の学びが誰かの為になれば、という想いで日々ブログを更新中。 PHP(CakePHP・Laravel・FuelPHP), Pyhotn(Django・Flask), C#(ASP.NET、Unity) が好きです。