オブジェクト指向プログラミングにおけるデザインパターンに、「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.