Greensleeves 2.0 / Archive / 2008-10 / 271743
Notes about XHTML, CSS, Flash, ActionScript, Javascript and I'm Interested in...
Iterator パターンをやってみる #1
今回は、前々から興味があったデザインパターンを触ってみることにします。まずは手始めに一番理解できそうだった Iterator パターンから。基本的な機能だけ実装していきます。#1とつけましたが、#2があるかは分かりませんっ。
Iterator パターンを理解する上で下記サイトが大変参考になりました。
- The Iterator Pattern: Flexible implementation of collections | ActionScript 3 Design Patterns
- AS3: Collection & Iterator
- デザインパターンに目覚める【閃光的網站・弛緩複合体 -Review Division-】
- Iterator パターンデザインパターン[モデリング] -TECHSCORE-
- Iteratorパターン for AS2.0 by 9server.net
おぼろげながら概要を把握したので、手を動かしていきます。
Iterator パターンとは
Iterator パターンとは GoF によってまとめられた 23 個のデザインパターンのうちの一つです。Java のものとして提案されたようですが、ActionScript でも使えるようです。
Iterator パターンは、コレクション(要素の集合体)とコレクションを走査するイテレータ(反復子)とでできています。実装の目的はプログラム本体から繰り返し処理を切り離し、コレクションへのアクセス部分は同一のインターフェースを用いることで、コレクションの内容や構造が変更されたとしても、プログラム本体への影響を少する狙いがあります。
インターフェースを定義
というわけで、まずはインターフェースから定義していきます。コレクションのインターフェースである ICollection とイテレータのインターフェースの IIterator です。ちなみに ActionScript 2.0 です。
ICollection.as
interface ICollection {
public function createIterator():IIterator;
}
最低限、イテレータを生成してコレクションを渡す createIterator メソッドを実装します。
IIterator.as
interface IIterator {
public function next():Object;
public function hasNext():Boolean;
}
最低限、次の要素を取得するする、next メソッドと、次の要素の存在を判定する hasNext メソッドを実装します。
インターフェースを実装する
上記で定義したインターフェースを実装していきます。あ、でもどういうプログラムを組むのか決めなきゃ。
- (飲食店の)メニュー単品を表す Menu クラス。
- Menu を集約する MenuList クラス。コレクションに当たる。
- MenuList クラスを走査する、MenuListIterator クラス。イテレータに当たる。
MenuListIterator クラスで MenuList クラスを走査し、Menu クラスの内容を出力します。
Menu.as
class Menu {
private var _name:String;
private var _value:Number;
//コンストラクタ
public function Menu(name:String, value:Number) {
_name = name;
_value = value;
}
//メニューの名前を返す
public function get name():String {
return _name;
}
//メニューの値段を返す
public function get value():Number {
return _value;
}
}
MenuList.as
class MenuList implements ICollection {
private var _list:Array;
// コンストラクタ
public function MenuList() {
this._list = [];
}
// メニューをコレクションに追加
public function addMenu(menu:Menu) {
this._list.push(menu);
}
// 任意の位置のMenuを返す
public function getMenuAt(index:Number ):Menu {
return this._list[index];
}
// コレクションの長さを返す
public function getLength():Number {
return this._list.length;
}
// イテレータを生成してコレクションを渡す
public function createIterator():IIterator {
return new MenuListIterator(this);
}
}
getMenuAt メソッドと getLength メソッドの機能はイテレータ側で実装しようかと考えたのですが、よくよく考えるとイテレータはコレクションを走査するのが目的なので、要素の取り出しはコレクション側にまとめようと思った次第です。
MenuListIterator.as
class MenuListIterator implements IIterator {
private var _collection:MenuList;
private var _index:Number;
//コンストラクタ
public function MenuListIterator(collection:MenuList) {
this._collection = collection;
this._index = 0;
}
//次の要素を取得する
public function next():Object {
return this._collection.getMenuAt(_index++);
}
//次の要素の存在を調べる
public function hasNext():Boolean {
return this._collection.getLength() > this._index;
}
}
イテレータ部分です。プライベート変数 _index を next メソッドにアクセスするたびにカウントアップしてコレクションを走査します。
実際に使ってみる
fla ファイルの _root の第一フレームに
// コレクションを生成
var menuList = new MenuList();
// メニューリストにメニューを突っ込む
menuList.addMenu(new Menu("ハンバーグ定食", 500));
menuList.addMenu(new Menu("からあげ定食", 480));
menuList.addMenu(new Menu("とんかつ定食", 550));
menuList.addMenu(new Menu("カレー", 380));
// イテレータを生成
var itr:MenuListIterator = menuList.createIterator();
// 走査する。
while(itr.hasNext()){
var menu = itr.next();
trace(menu.name + ": " + menu.value + "円");
/*
ハンバーグ定食: 500円
からあげ定食: 480円
とんかつ定食: 550円
カレー: 380円
*/
}
きちんと走査できているようです。コードも簡潔になってよいですね。仮にメニューのリストが XML で提供されたとしても、コレクションとイテレータをキチン編集すれば、while 文の中身、つまりコレクションにアクセスしてデータを呼び出している部分は編集しなくて済むはずです。
何か変なところがあれば、ご指摘をいただければ!
この記事に対するコメントはまだありません。