PHP

PHPで使うGoFパターン ひとり Advent Calendar - コマンド

More than 5 years have passed since last update.


コマンドってなに?

要するにコマンド(命令)を統一シグニチャでクラスにまとめて、

一連で実行出来るような仕組みとして実装するパターンです。

身近なところの実装例としては以下


  • undo, redo

  • エディタのキーボードマクロ(場合によってはスクリプト保存付きも可能)

  • キュー管理のバッチジョブコントローラ


コマンドパターンの構造


  • Invoker - コマンドをスタックに保持したり、コマンドを実行したり

  • Receiver - コマンドに操作される対象のクラス。内部に状態を持っていてCommandの操作によって内部の状態が変化したりする

  • Client - Invokerを操作するコントローラ

  • Command - 実コマンド。Receiverを操作することでコマンドを実現。Invokerに利用される

<?php

/* Reciever */
class FileSystem {
private $_Path = null;

public function __construct ( $path ) {
$this->_Path = $path;
}

// コマンドに利用されるAPIだよ
public function changeDirectory () {
return `cd $this->_Path`;
}

// コマンドに利用されるAPIだよ。
public function makeDirectory () {
return `mkdir $this->_Path`;
}

// コマンドに利用されるAPIだよ。
public function removeDirectory () {
return `rm $this->_Path`;
}

// コマンドに利用されるAPIだよ。
public function createFile () {
return `touch $this->_Path`;
}
}

<?php

/* command interface */
interface ICommand {
public function execute ();
public function undo ();
}

/* 実コマンド */
class MakeDirectoryCommand implements ICommand {
private $_FileSystem = null;

public function __construct ( $fs ) {
$this->_FileSystem = $fs;
}

public function execute () {
$this->_FileSystem->makeDirectory ();
}

public function undo () {
$this->_FileSystem->removeDirectory ();
}
}


class RemoveDirectoryCommand implements ICommand {

private $_FileSystem = null;
public function __construct ( $fs ) {
$this->_FileSystem = $fs;
}

public function execute () {
$this->_FileSystem->removeDirectory ();
}

public function undo () {
$this->_FileSystem->makeDirectory ();
}
}

<?php

/* Client */
class ShellScript {
private $_Queue = array ();
private $_Position = 0;

public function add ( $cmd ) {
$this->_Queue[] = $cmd;
}

// // MEMO: 実行途中にnextしたり、undoしてポジションを戻して
// // 中途からコマンド上書きしたりも出来るよ
// public function next () {
// return ++$this->_Position;
// }
// public function undo ( $times = 1 ) {
// $this->_Queue[$this->_Position]->undo();
// for ( $i = 0; $times < $i; $i++ ) $this->_Position--;
// }

//
// でも今回はサンプルなのでリニアに実行するだけにするよ
//
public function run () {
foreach ( $this->_Queue as $q ) $q->execute();
}
}

<?php

/* Clientです。クラスじゃないけど */
$sh = new ShellScript();
$fs = new FileSystem ( '/etc/tmp/sample' );
$sh->add ( new MakeDirectoryCommand ( $fs ) );
$sh->add ( new RemoveDirectoryCommand ( $fs ) );
$sh->run ();