pblog

pplog.net を作っている @ppworks こと越川直人(Koshikawa Naoto)のブログ。esa LLCで働いてます(\\( ⁰⊖⁰)/)

Advanced ActionScript3.0 with Design Patternsの邦訳版を読んだメモ

ついに邦訳版が出ましたので、購入。
ActionScript 3.0 : デザインパターン (ADOBE TECH LAB)
薄い本なのでサラッと読めそうな気がします。読みながらのメモです。良く分からなかったので調べたこととかも書いておきます。
一部、訳が若干残念な感じなので英語版と一緒に読むことで理解が深まります。商売上手ですね。



ActionScript 3.0 : デザインパターン (ADOBE TECH LAB)

http://www.amazon.co.jp/dp/4798118362

Advanced ActionScript3.0 with Design Patterns

http://www.amazon.co.jp/dp/0321426568


Chapter3 MVCパターン

MVCについては、wikipediaとかに載ってます。



Model View Controller - Wikipedia

http://ja.wikipedia.org/wiki/Model_View_Controller


モデル
  • ビュー、コントローラからは独立
  • モデル内のデータが変更された場合、変更メッセージをブロードキャストする
    • as3で実装するには、モデルはデータが変更された際、Event.CHANGEをdispachするなど
ビュー
  • 視覚的な要素とモデルデータとを読み取るロジック
  • UIからの要求を処理するロジック
  • モデルへの参照を持つ
コントローラ
  • 入力処理を行う
  • モデルとビューを更新
  • モデルへの参照を持つ
  • ユーザイベント、システムイベントを通じて、モデルのデータを変更する

図にするとこんな感じ
MVCパターン

ClockDataの例(053)

モデルでは、元データへの直接参照を避けるためにcloneメソッドを通してデータを受け取る直接オブジェクト参照をすると、モデル外での操作がデータモデルに影響してしまう。

public function clone():Hoge {
  return new Hoge(); // 現在の状態の複製が必要な際はnewだけじゃだめだと思う
}

Eventのハンドラを直接呼び出したい場合、ハンドラの引数の初期値をnullにしておく

public function Hoge()
{
  addEventListener(Event.CHANGE, changeHandler);
  
  changeHandler(); // 初回分を実行
}

private function changeHandler(event:Event=null):void
{

}
コントローラの追加の例(068)

自動で更新される時計の処理

  1. モデルがシステムイベントで更新される。
  2. ビューがそのイベントをリッスンしているのでビューが更新される
  3. コントローラがそのイベントをリッスンしているので、コントローラに定義されているUIが更新される

ユーザが入力した時刻に更新される時計の処理

  1. ユーザの入力でモデルを更新する
  2. ビューがそのイベントをリッスンしているのでビューが更新される
  3. コントローラがそのイベントをリッスンしているので、コントローラに定義されているUIが更新される(但し、ユーザの入力で既に変わっているの効果はない)

この例では、コントローラにUIのビューが混じっているので混乱しやすいかも知れません。私はもちろん混乱しました。

Chapter4 シングルトンパターン

075の以下の文章の意味が分かりません。


ActionScript3.0のひとつの特徴は、メソッドのすべての引数は、デフォルト設定が不要であるというものがあります。

原文は

One feature of ActionScript 3.0 is that all parameters of a method are now required unless a default value is provided.

私の怪しい英語力で訳すと

ActionScript3.0では、メソッドの引数にデフォルト値がない場合、その引数は必須となる。

といった感じですかね。それだと前後の文章が理解できます。

Settingsクラスの構築の例(081)

flash.utils.Proxyクラスの使いどころを、これで知りました。未定義のプロパティやメソッドを捕捉するために使うわけですね。

コンポジション経由でEventDispatcherを追加(082)

他のクラスをすでに継承しているけど、あのクラス(EventDispatcher)の機能も欲しい!という場合の解決策です。

public class Hoge extends Fugo implements IEventDispatcher
{
  private var _dispatcher:EventDispatcher;

  public function Hoge()
  {
    _dispatcher = new EventDispatcher();
  }

  public function addEventListener(type:String, handler:Function, 
    useCapture:Boolean, priority:int = 0, weakRef:Boolean = false):void
  {
    _dispatcher.addEventListener(type, handler, useCapture, priority, weakRef);
  }

  // ... IEventDispatcherに必要なメソッドを実装
}

Chapter5 テンプレートメソッドパターンとファクトリーメソッドパターン

この本で一番長い章です、タイトルが。
ActionScript3.0はasbstractがないので、抽象メソッド内で例外を発生させる方法がありますと書いてありますが例がないので、例はこんな感じ。

package
{
  public class AbstractClass 
  {
    /**
     * constructor
     */
    public function AbstractClass() 
    {
      if (Object(this).constructor == AbstractClass)
        throw(new Error("you can't create instance of AbstractClass."));
    }
  }
}

本書で指摘されているように、これだと実行時にエラーを発生させるだけなんですよね。なので、命名規則をちゃんとして間違えてnewしちゃったらコンパイル時に気づいてね、みたいな感じで。

Chapter6 プロキシパターン

イメージローダの件が例として挙げられていますが、イメージの幅や高さはEvent.INITイベントが発生するまで取得出来ないので注意が必要です。(当たり前?)

flash.events.DataEventって何?


DataEvent オブジェクトは、生データのロードが完了したときにオブジェクトによって送出されます。 次の 2 種類のデータイベントがあります。

  • DataEvent.DATA:送受信されたデータのために送出されます。
  • DataEvent.UPLOAD_COMPLETE_DATA:データが送信され、サーバーが応答したときに送出されます。

flash.events.DataEventのasdoc


とのこと。このデザパタ本の中では下記のように使っています。

dispatchEvent(new DateEvent(Event.COMPLETE, false, false, <<渡したいデータ>>);

これでhandler側から「渡したいデータ」にアクセス出来ます。私は普段、dispatchEventを送出する側にプロパティを持たせて、handler側からはevent.target.propertyNameを参照するようなやり方が多いです。でもこうやってデータ渡せるのは便利ですね。しかも組み込みイベント。どっかで使ってみたいと思います。

flash.utils.Proxy

AS1, 2に存在したObjectクラスの_resolveメソッドは未定義オブジェクトの呼び出しを捕捉することが出来ました。
この機能はAS3から、flash.utils.Proxyに組み込まれました。flash.utils.Proxyクラスを継承することで未定義オブジェクトの呼び出しを捕捉します。

  • 未定義プロパティの捕捉には、getPropertyメソッドをオーバーライドします。
  • 未定義メソッドの捕捉には、callPropertyメソッドをオーバーライドします。

AS3では多重継承が出来ないため、Proxyクラスを継承したクラスで他のクラスも継承したい場合には、コンポジションによって機能を追加します。先の「コンポジション経由でEventDispatcherを追加」はこれです。

アダプタパターンとファサードパターン

アダプタパターンは、対象のAPIを変換すること。ファサードパターンは、呼び出しを単純化することです。めもめも。

Chapter7 イテレータパターン

コレクションとイテレータの役割分担が大事。いままでちゃんと分けていなかったので反省です。

Chapter8 コンポジットパターン

インターフェースの実装を書く際に、毎回冗長なコードを書く可能性がある場合は、基本クラスを実装する。そして差分を継承先のクラスで書いていくようにしましょう。
インターフェースを使い始めのころは、まじめに1つ1つのメソッドを実装していて気づくのですが、共通部分があったりするんですよね。だったら、まずは抽象クラスで実装してそっから継承しようという話です。

ところで、126の以下の文章の意味が分かりません。


リーフエレメントはコンポジットエレメントになることができますが、コンポジットエレメントはリーフエレメントになることはできません。

原文は

Leaf elements can be placed in composite elements, but composite elements cannot be placed in leaf elements.

私の怪しい英語力で訳すと

リーフエレメントはコンポジットエレメントの中に配置できます。逆にコンポジットエレメントはリーフエレメントの中に配置出来ません。

といった感じですかね。つまり逆の意味になってしまっていますので注意。

Chapter9 デコレータパターン

デコレートパターンは、「マトリョーシカ」に例えることが可能とはいえ、マトリョーシカって?



マトリョーシカ人形

http://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%88%E3%83%AA%E3%83%A7%E3%83%BC%E3%82%B7%E3%82%AB

Decoratorパターン

http://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3


本チャプターで登場する「デコレート」は原書では「Decorated」と書かれています。つまり、デコレータがデコレーションする対象を指します。デコレータがデコーレトをなんやかんやという記述が繰り返されていると何が何やら分からなくなりそうです。上記のwikipediaにある図のComponentが本書の「デコレートオブジェクト」に当たります。

Chapter10 コマンドパターン



Command パターン

http://ja.wikipedia.org/wiki/Command_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3



CommandStackのputCommandでなぜspliceするのか疑問でしたが、nextメソッドとprevメソッドを見たら分かりました。prevとnextで_indexが変わるので、prevしている最中にputCommandした場合には、それ以降に存在していた履歴を全部消す必要がありますね。

図にすると、こんな感じ。
undoしてからputCommandしたらその先のコマンドは消す

本章後半にプロキシミティゲームの構築という節があるのですが、何故か理解できなくて何だろうと思っていたら、肝心なGameboardDataクラスの説明がすっぽり抜け落ちているではありませんか。各所に突然、GameboardDataクラスが登場していますが、定義が載っていません。

Chapter11 メメントパターン

  • メメントオブジェクトにオリジネーターの状態を保存しておく
  • オリジネータにはメメントを取得・設定するメソッドを実装する必要がある

Chapter12 ステートパターン

Chapter13 イベント処理

イベント処理に関しては詳説 ActionScript 3.0を読んでおけば良い感じです。特筆することはありませんでした。

252の以下の表現がちょっと気になりました。


stopPropagation()はcurrentTargetのリスナへのイベント通知は終了させますが、それ以上のディスプレイリスト探索を中止します。

これは原文

If you call stopPropagation(), the event is not allowed to continue up the display list , but the currentTarget is allowed to finish dispatching the event to its listeners.

を直訳した感じなのですが、イベント通知は終了の意味がちょっと分かりづらいですね。

stopPropagation()はcurrentTargetに登録されたリスナへのイベント通知は送出します。但し、それ以降のイベントフローに存在するDisplayObject Listのイベント送出を中止します。

という意味だと思います。

Chapter14 データの転送

Flash Remoting

NetConnectionを使う。call時の第二引数でRespoderインスタンスを指定することで、サービス側でreturnする値を受け取ることが可能。コールバック関数は

  • 正常なレスポンスを取得した際はResponder生成時の第一引数の関数
  • サービス側に指定したメソッドがない場合などエラーが発生した際は、Responder生成時の第二引数の関数

が呼ばれる。

var nc:NetConnection = new NetConnection();
nc.connect("<<gateway URL>>");
nc.call("<<service name>>.<<method name>>", new Responder(result, error); // result, errorはコールバック

Chapter15 E4X

E4Xに関しては詳説 ActionScript 3.0を読んでおけば良い感じです。特筆することはありませんでした。
意外なE4Xの使いどころといえば、CDATA SectionでJavaScript APIなんかもあります。

Chapter16 正規表現

コンストラクタでのバックスラッシュ(\)

コンストラクタでバックスラッシュ(\)を扱う場合、エスケープが必要となります。

var regexp1:RegExp = new RegExp("^\d+$"); // \dという文字列でマッチ
var regexp2:RegExp = new RegExp("^\\d+$"); // \dは数値を表す文字クラスなので数値にマッチ
var regexp3:RegExp = /^\d$/; // \dは数値を表す文字クラスなので数値にマッチ

感想

デザインパターンの知識が多少なりともあるほうが読みやすいと思います。デザインパターンの書籍として以下のような書籍を読んでから望むと良いかもしれません。



増補改訂版Java言語で学ぶデザインパターン入門

http://www.amazon.co.jp/dp/4797327030

Head Firstデザインパターン―頭とからだで覚えるデザインパターンの基本

http://www.amazon.co.jp/dp/4873112494

PHPによるデザインパターン入門

http://www.amazon.co.jp/dp/4798015164


その他、AS3の知識ももちろん必要です。AS3の書籍は以下のものがおすすめです。



初めてのActionScript 3.0 Flashユーザーのためのステップアップガイド

http://www.amazon.co.jp/dp/4873113717

詳説 ActionScript 3.0

http://www.amazon.co.jp/dp/4873113873


おまけ

おれおれ正誤表を書いておきます。公式のものではないので参考程度にとどめて下さい。

正誤表
page
056 まず、Clockという抽象ベースクラス まず、AbstractClockViewという抽象ベースクラス
057 public function Clock public function AbstractClockView
066 date.milliseconds = getTimer()._startTime; date.milliseconds = getTimer() - _startTime;
070 view.y = 40.view.getBounds(view).top; view.y = 40 - view.getBounds(view).top;
075 ActionScript3.0のひとつの特徴は、メソッドのすべての引数は、デフォルト設定が不要であるというものがあります。 ActionScript3.0では、メソッドの引数にデフォルト値がない場合、その引数は必須となります。
098 プロキシとしてLloaderを生成 プロキシとしてLoaderを生成
126 リーフエレメントはコンポジットエレメントになることができますが、コンポジットエレメントはリーフエレメントになることはできません。 リーフエレメントはコンポジットエレメントの中に配置できます。逆にコンポジットエレメントはリーフエレメントの中に配置出来ません。
144 public function ReaderDecorator public function AbstractReaderDecorator
145 extends ReaderDecorator extends AbstractReaderDecorator(本書ではこうあるべきですが、DL出来るサンプルファイルはすべてReaderDecoratorになってます。)
210 11.1.5.mailクラス 11.1.5 mainクラス
252 stopPropagation()はcurrentTargetのリスナへのイベント通知は終了させますが、それ以上のディスプレイリスト探索を中止します。 stopPropagation()はcurrentTargetに登録されたリスナへのイベント通知は送出します。但し、それ以降のイベントフローに存在するDisplayObject Listのイベント送出を中止します。
309 ^ その行の先頭(mフラグ設定時) ^ mフラグ未設定時には文字列の先頭、mフラグ設定時にはその行の先頭
309 $ その行の末尾(mフラグ設定時) $ mフラグ未設定時には文字列の末尾、mフラグ設定時にはその行の末尾

その他

page
062 return value.toString();がインデントされていない
115 UIntCollectionコンストラクタの_index = 0;がインデントされていない
120 addElementメソッドブロックの終わりの}がインデントされていない
191 GameboardDataクラスが突然登場するが紙面にGameboardDataクラスが掲載されていない
196 private var _pieces:Spriteが定義されていない