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

観察者パターン。観察対象に観察者を登録しておいて、状態が変化したときに観察者に通知をもらうことで、観察者が観察対象の状態に応じて処理を行います。

非常に受動的な観察です。これが改良されて、AS3 の addEventListener の考えの元になったデザインパターンなのかもしれません。addEventListener は能動的な観察と言えるかもしれないからです。

ActionScript: Subject.as

package jp.feb19.gof.observer
{
	import flash.utils.Dictionary;
	
	public class Subject
	{
		private var _observers:Dictionary;
		
		public function Subject()
		{
			_observers = new Dictionary();
		}
		
		public function addObserver( observer:Observer ):void
		{
			_observers[observer.getName()] = observer;
		}
		
		public function deleteObserver( observer:Observer ):void
		{
			delete _observers[observer.getName()];
		}
		
		public function notifyObservers():void
		{
			for each(var observers:Observer in _observers)
			{
				observers.update(this);
			}
		}
		
		public function getNumber():Number
		{
			return 0;
		}
		
		public function execute():void
		{
			
		}
	}
}

ActionScript: Observer.as

package jp.feb19.gof.observer
{
	public class Observer
	{
		public function Observer()
		{
		}
		
		public function update(subject:Subject):void
		{
			
		}
		
		public function getName():String
		{
			return "";
		}
	}
}

Subject サブクラス

ActionScript: ConcreteSubject.as

package jp.feb19.gof.observer
{
	public class ConcreteSubject extends Subject
	{
		private var _number:Number;
		
		public function ConcreteSubject()
		{
			super();
		}
		
		override public function getNumber():Number
		{
			return _number;
		}
		
		override public function execute():void
		{
			for (var i:int= 0; i < 5; i++)
			{
				_number = Math.floor(Math.random() * 30);
				notifyObservers();
			}
		}
	}
}

Observer サブクラス

ActionScript: DigitObserver.as

package jp.feb19.gof.observer
{
	public class DigitObserver extends Observer
	{
		public function DigitObserver()
		{
			super();
		}
		
		override public function update(subject:Subject):void
		{
			trace(subject.getNumber());
		}
		
		override public function getName():String
		{
			return "DigitObserver";
		}
	}
}

ActionScript: GraphObserver.as

package jp.feb19.gof.observer
{
	public class GraphObserver extends Observer
	{
		public function GraphObserver()
		{
			super();
		}
		
		override public function update(subject:Subject):void
		{
			var t:String = "";
			var l:int = subject.getNumber();
			for (var i:int = 0; i < l; i++)
			{
				t += "*";
			}
			
			trace(t);
		}
		
		override public function getName():String
		{
			return "GraphObserver";
		}
	}
}

テストクラス

ActionScript: ObserverTest.as

package jp.feb19.gof.observer
{
	import flash.display.Sprite;
	
	public class ObserverTest extends Sprite
	{
		public function ObserverTest()
		{
			super();
			
			var subject:Subject = new ConcreteSubject();
			var observer1:Observer = new DigitObserver();
			var observer2:Observer = new GraphObserver();
			subject.addObserver(observer1);
			subject.addObserver(observer2);
			subject.execute();
		}
	}
}

出力

***************************
27
***
3
******
6
******************
18
*********************
21

GoF デザインパターンではないですが、MVC というデザインパターンにも活用できそうです。 Model が変化したあと View が必ず更新される場合、Model と View の関係に対応できたりするかもしれません。

なお、Observer の update メソッドが呼ばれる順番が変わっても問題が起きないような実装にすべきです。