目的

要求をオブジェクトとしてカプセル化することによって、異なる要求や、要求からなるキューやログにより、クライアントをパラメータ化する。また、取り消し可能なオペレーションをサポートする。


Command

package
{
	import flash.display.Sprite;
 
	public class Main extends Sprite
	{
		public function Main()
		{
			var sprite:Sprite = new Sprite();
			sprite.graphics.lineStyle(2.0, 0x0);
			sprite.graphics.moveTo(100, 100);
			sprite.graphics.lineTo(200, 200);
			addChild(sprite);
		}
	}
}
 
例えば線を描く操作を行いたい場合、上記コードのような一連の処理を記述します。


package
{
	import flash.display.Graphics;
	import flash.geom.Point;
 
	public class DrawLine
	{
		private var graphics:Graphics;
		private var from:Point;
		private var to:Point;
 
		public function DrawLine(graphics:Graphics, from:Point, to:Point)
		{
			this.graphics = graphics;
			this.from = from;
			this.to = to;
		}
 
		public function execute():void
		{
			graphics.lineStyle(2.0, 0x0);
			graphics.moveTo(from.x, from.y);
			graphics.lineTo(to.x, to.y);
		}
	}
}
 
その一連の処理を一つのクラスに収めたものを"コマンド"と呼びます。

コマンドは実行にあたるメソッドを呼び出さない限り、一連の処理が実行されることはありません。
なので、コマンドクラスのオブジェクトを配列やキューに入れて管理しておき、
  • 任意のタイミングでのコマンド実行
  • コマンドの追加/削除/変更
などのような操作を行うことが出来ます。

このような構造を作るのがCommandパターンの目的で、Commandパターンを利用すると、
  • 操作履歴
  • 取り消し/やり直し
  • リプレイ
  • マクロ
などの処理が実装できます。


プログラム

Receiverクラス
package 
{
	public class Receiver
	{
		public function leftTurn():void
		{
			trace("左を向く");
		}
 
		public function rightTurn():void
		{
			trace("右を向く");
		}
	}
}
 
コマンドクラスに呼び出されることによって、実際の処理を行うクラスのことをReceiverといいます。
最初に説明したコード例で言うと、GraphicsクラスがReceiverになります。

Command(クラス or インターフェース)
package
{
	public class Command
	{	
		protected var receiver:Receiver;
 
		public function Command(receiver:Receiver)
		{
			this.receiver = receiver;
		}
 
		// Receiverに対してこういう処理をしてくれと命令を与える(Receiverのメソッドを呼ぶ)
		public function action():void
		{
			// 子が実装するので何も書かない(例外を投げても良い)
		}
	}
}
 
Receiverに命令を与えるCommandクラス(インターフェース)です。
実際に命令を与えるのは、このCommandクラスを継承した子クラスで、このクラスはどういうプロパティやメソッドを持てばいいのかを決める仕事をしています。

ConcreteCommandクラス
package  
{
	public class LeftTurnCommand extends Command
	{
		public function LeftTurnCommand(receiver:Receiver) 
		{
			super(receiver);
		}
 
		override public function action():void
		{
			receiver.leftTurn(); // 左へターンせよという命令を出す
		}
	}
}
 
package  
{
	public class RightTurnCommand extends Command
	{
		public function RightTurnCommand(receiver:Receiver) 
		{
			super(receiver);
		}
 
		override public function action():void
		{
			receiver.rightTurn(); // 右へターンせよという命令を出す
		}
	}
}
 
Commandを実装/継承したクラスのことをConcreteCommandクラスと呼び、実際にReceiverに対して命令を与えます。

Invoker
package  
{
	public class Invoker
	{
		// 複数のCommandを入れるVector
		private var commands:Vector.<Command> = new Vector.<Command>();
 
		// Commandを追加
		public function addCommand(command:Command):void
		{
			commands.push(command);
		}
 
		// execite()を呼び出すと、addした全てのCommandを実行する
		public function execute():void
		{
			for each (var command:Command in commands)
			{
				command.action();
			}
		}
	}
}
 
複数のコマンドを管理するためのデータ構造を持つInvokerクラスです。
上記コード例では、execute()を呼び出すと、addした全てのCommandを実行していますが、呼ぶたびに先頭コマンドから順に実行する形でも構いません。

Main
package
{
	import flash.display.Sprite;
 
	public class Main extends Sprite
	{
		public function Main()
		{
			var receiver:Receiver = new Receiver();
			var invoker:Invoker = new Invoker();
			invoker.addCommand(new LeftTurnCommand(receiver));
			invoker.addCommand(new LeftTurnCommand(receiver));
			invoker.addCommand(new RightTurnCommand(receiver));
			invoker.execute();
		}
	}
}
 
左を向く
左を向く
右を向く
 
実行結果です。

Invokerクラスのコード例
package  
{
	public class Invoker
	{
		private var _commands:Vector.<Command> = new Vector.<Command>();
		public function get commands():Vector.<Command> { return _commands; }
 
		// 単一コマンドを追加
		public function addCommand(command:Command):void
		{
			_commands.push(command);
		}
 
		// コマンド列を追加
		public function addCommands(commands:Vector.<Command>):void
		{
			_commands = _commands.concat(commands);
		}
 
		// 実行順序を反転させる
		public function reverse():void
		{
			_commands = _commands.reverse();
		}
 
		// 先頭にあるコマンドを実行する。実行できたらtrue、できなかったらfalseを返す
		public function execute():Boolean
		{
			var command:Command = _commands.shift();
			if (command)
			{
				command.action();
				return true;
			}
			else
			{
				return false;
			}
		}
 
		// 全てのコマンドを実行する。引数がtrueなら実行後にコマンド列を全て削除する。
		public function executeAll(deleteCommands:Boolean = false):void
		{
			for each (var command:Command in _commands)
			{
				command.action();
			}
 
			if (deleteCommands) _commands.length = 0;
		}
	}
}
 

|新しいページ|検索|ページ一覧|RSS|@ウィキご利用ガイド | 管理者にお問合せ
|ログイン|