オブジェクト指向プログラミングにおけるデザインパターンに、「Observerパターン」があります。対象オブジェクトの状態が変わるごとに、その情報を必要なインスタンス(Observer)に送る仕組みです。Javaの例をActionScript 3.0に書替えてみましょう。今回は、デザインパターンの解説書として定評のある結城浩著『Java言語で学ぶデザインパターン入門』のサンプルプログラムを題材とします[*1]。
Observerパターンは、出版−購読型モデルとも呼ばれます。メールマガジンに登録して、その配信を受けるのと似た仕組みです。したがって、配信する側(Generator)と受取る側(Observer)のふたつの役割が必要になります。ActionScript 3.0のクラス設計では、インターフェイスで仕様を定めます。
配信側のインターフェイスには、少なくとも3つのメソッドを備える必要があります(スクリプト001)。
package { public interface INumberGenerator { // 配信クラスに必要なメソッド function addObserver(observer:IObserver):void; function deleteObserver(observer:IObserver):void; function notifyObservers():void; // サンプルとして加えたメソッド function getNumber():int; function execute():void; } }
Javaと異なりActionScript 3.0のインターフェイスでは宣言できませんが、受信するオブザーバーのインスタンス(後述インターフェイスIObserverを実装)を納めるプロパティも用意します。また、オブザーバーへの配信は、そのインスタンスのメソッド(後述update())を呼出して行います。
受信側のインターフェイスは、配信側クラスのメソッドnotifyObservers()から呼出されるupdate()を宣言します(スクリプト002)。
スクリプト002■インターフェイスIObserverpackage { public interface IObserver { function update(generator:INumberGenerator):void; } }
メソッドupdate()は、引数に配信するクラス(インターフェイスINumberGeneratorを実装)のインスタンスを受取ることにしました。したがって、必要な情報はこのインスタンスから(getNumber()メソッドにより)取出します。
配信側のクラスRandomNumberGeneratorは、つぎのようにインターフェイスINumberGeneratorを実装します(スクリプト003)。
スクリプト003■クラスRandomNumberGeneratorpackage { import flash.utils.setInterval; import flash.utils.clearInterval; public class RandomNumberGenerator implements INumberGenerator { private var observers:Vector.<IObserver> = new Vector.<IObserver>(); private var number:int; private var id:uint; private var interval:uint = 1000; public function RandomNumberGenerator() {} public function addObserver(observer:IObserver):void { observers.push(observer); } public function deleteObserver(observer:IObserver):void { var nLength:uint = observers.length; for (var i:uint = 0; i < nLength; i++) { var element:IObserver = observers[i]; if (element == observer) { observers.splice(i, 1); break; } } } public function notifyObservers():void { var nLength:uint = observers.length; number = Math.floor(Math.random() * 50); for (var i:uint = 0; i < nLength; i++) { var observer:IObserver = observers[i]; observer.update(this); } } public function getNumber():int { return number; } public function execute():void { clearInterval(id); id = setInterval(notifyObservers, interval); } } }
受信するオブザーバーのインスタンスは、Vectorインスタンスのプロパティ(observers)に納めることにしました。オブザーバーの登録と削除のメソッドaddObserver()およびdeleteObserver()は、それに即した処理にしています。また、配信のメソッドnotifyObservers()は、オブザーバーのインスタンスすべてを取出して、自身を引数にそのupdate()メソッドを呼出します。そのとき、ランダムな整数(0〜49)がプロパティ(number)に設定されます。
メソッドexecute()は、配信を開始します。一定の時間間隔(1000ミリ秒)でメソッドnotifyObservers()を呼出します[*2]。受信側のオブザーバーインスタンスは、getNumber()メソッドでランダムな整数が取出せます。
受信するオブザーバーのクラスDigitObserverは、以下のようにインターフェイスIObserverを実装します(スクリプト004)。定義するのはメソッドupdate()のみです。
スクリプト004■クラスDigitObserverpackage { public class DigitObserver implements IObserver { public function DigitObserver() {} public function update(generator:INumberGenerator):void { trace("DigitObserver: " + generator.getNumber()); } } }
メソッドupdate()は、引数に受取った配信側インスタンスのgetNumber()メソッドによりランダムな整数を取出し、trace()関数でその値を[出力]します。
これで必要なインターフェイスおよびクラスは揃いました。確認用のドキュメントクラスMainを以下のように定義して[ムービープレビュー]で試しましょう(スクリプト005)。ランダムな整数が1秒ごとに[出力]されます(図001)。
スクリプト005■ドキュメントクラスMainpackage { import flash.display.Sprite; public class Main extends Sprite { public function Main() { var generator:INumberGenerator = new RandomNumberGenerator(); var observer0:IObserver = new DigitObserver(); generator.addObserver(observer0); generator.execute(); } } }
図001■ランダムな整数が1秒ごとに[出力]される
デザインパターンのつねとして、同じインターフェイスさえ実装すれば、具体的な処理内容は変えられます。受信するオブザーバーのクラスをもうひとつGraphObserverとして定義しました(スクリプト006)。
スクリプト006■クラスGraphObserverpackage { public class GraphObserver implements IObserver { public function GraphObserver() {} public function update(generator:INumberGenerator):void { var result_str:String = "GraphObserver: "; var count:int = generator.getNumber(); for (var i:uint = 0; i < count; i++) { result_str += "*"; } trace(result_str); } } }
テスト用のサンプルですので、前掲クラスDigitObserverと変えた点はごくわずかです。取出したランダムな整数を数値でなく、"*"の数でグラフのように[出力]します(図002)。ドキュメントクラスMainには、つぎのスクリプト006のようにステートメントを加えます。
スクリプト007■修正したドキュメントクラスMainpackage { import flash.display.Sprite; public class Main extends Sprite { public function Main() { var generator:INumberGenerator = new RandomNumberGenerator(); var observer0:IObserver = new DigitObserver(); var observer1:IObserver = new GraphObserver(); generator.addObserver(observer0); generator.addObserver(observer1); generator.execute(); } } }
図002■ランダムな整数とそのグラフが1秒ごとに[出力]される
なお、「Observer(オブザーバー)パターン」はイベントリスナーを例に説明しています。
[*1] ActionScript 3.0への書替えは、筆者の解釈にもとづきます。
[*2] イベントリスナーはObserverパターンなので使わずに、あえてsetInterval()メソッドを用いました。
この記事へのコメント
●1.Exercise Balls(2010年12月16日 01:40)
Thanks for an idea, you sparked at thought from a angle I hadn’t given thoguht to yet. Now lets see if I can do something with it.