ActionScript 3 (Flash/AIR): GoF デザインパターン – Chain of Responsibility

責任のたらい回しパターン。自分に与えられた問題が処理不能な場合は次に答える人に問題を投げ渡していくパターンです。

オーバーヘッドが高いので高速な処理は望めませんが、AS3 のイベントチェーンもまさにこのデザインパターンが使われています。MouseEvent.CLICK されたけど、ハンドラーが存在しない、またはイベントを止められない場合は上の階層、上の階層へ、イベントを伝えていく考え方に該当します。

同じ責任を扱う一連のクラスでありながら、お互いの事は気にする必要がないのが、このデザインパターンの利点で、新たな責任処理クラスを簡単に追加できます。

ActionScript: Handler.as

package jp.feb19.gof.chainofresponsibility
{
	public class Handler
	{
		protected var _successor:Handler;
		
		public function Handler()
		{
			
		}
		
		public function get successor():Handler
		{
			return _successor;
		}
		public function set successor(value:Handler):void
		{
			_successor = value;
		}
		
		public function handleRequest(request:Request):void
		{
			// サブクラスで定義
		}
	}
}

ActionScript: FirstChoiceUniversity.as

package jp.feb19.gof.chainofresponsibility
{
	public class FirstChoiceUniversity extends Handler
	{
		public function FirstChoiceUniversity()
		{
			super();
		}
		
		override public function handleRequest(request:Request):void
		{
			if (request.score > 70)
			{
				trace("合格: " + request.score);
			}
			else if (this.successor != null)
			{
				trace("不合格。滑り止めへ: " + request.score);
				this.successor.handleRequest(request);
			}
			else
			{
				trace("不合格。また来年。");
			}
		}
	}
}

ActionScript: SecondChoiceUniversity.as

package jp.feb19.gof.chainofresponsibility
{
	public class SecondChoiceUniversity extends Handler
	{
		public function SecondChoiceUniversity()
		{
			super();
		}
		
		override public function handleRequest(request:Request):void
		{
			if (request.score > 60)
			{
				trace("合格: " + request.score);
			}
			else if (this.successor != null)
			{
				trace("不合格。滑り止めへ: " + request.score);
				this.successor.handleRequest(request);
			}
			else
			{
				trace("不合格。また来年。");
			}
		}
	}
}

ActionScript: ThirdChoiceUniversity.as

package jp.feb19.gof.chainofresponsibility
{
	public class ThirdChoiceUniversity extends Handler
	{
		public function ThirdChoiceUniversity()
		{
			super();
		}
		
		override public function handleRequest(request:Request):void
		{
			if (request.score > 50)
			{
				trace("合格: " + request.score);
			}
			else if (this.successor != null)
			{
				trace("不合格。滑り止めへ: " + request.score);
				this.successor.handleRequest(request);
			}
			else
			{
				trace("不合格。また来年。");
			}
		}
	}
}

ActionScript: Request.as

package jp.feb19.gof.chainofresponsibility
{
	public class Request
	{
		private var _score:int;
		
		public function Request(score:int)
		{
			_score = score;
		}
		
		public function get score():int
		{
			return _score;
		}
	}
}

テストクラス

ActionScript: ChainOfResponsibilityTest.as

package jp.feb19.gof.chainofresponsibility
{
	import flash.display.Sprite;
	
	public class ChainOfResponsibilityTest extends Sprite
	{
		public function ChainOfResponsibilityTest()
		{
			super();
			
			var firstChoice:Handler = new FirstChoiceUniversity();
			var secondChoice:Handler = new SecondChoiceUniversity();
			var thirdChoice:Handler = new ThirdChoiceUniversity();
			
			firstChoice.successor = secondChoice;
			secondChoice.successor = thirdChoice;
			
			trace("--");
			var request:Request = new Request(66);
			firstChoice.handleRequest(request);
			
			trace("--");
			request = new Request(55);
			firstChoice.handleRequest(request);
			
			trace("--");
			request = new Request(45);
			firstChoice.handleRequest(request);
		}
	}
}

出力

--
不合格。滑り止めへ: 66
合格: 66
--
不合格。滑り止めへ: 55
不合格。滑り止めへ: 55
合格: 55
--
不合格。滑り止めへ: 45
不合格。滑り止めへ: 45
不合格。また来年。

デメリットはやっぱりあまりにも高いオーバーヘッド。なのであんまり使い道は無いかもしれません。柔軟な感じのフレームワークを作る時に検討材料になりそうな感じです。