やっと9回目。今回はBridgeパターンです。移植作業は簡単でした。
ポイントはクラスを機能と実装の階層に分けて橋渡しをさせるような作りにしておき、後々、修正するときにどっちの階層をいじるのか明確にしておくと混乱が少ないですねというパターン。ふーん。
とはいえ、機能と実装の階層とか言われてもよくわからん。テキストではともて手際よく解説されていたので、自分なりにわかる形でメモっておこう。なんか学校の講義ノートっぽくなってきた。
機能の階層
大まかな機能を持ったクラスを拡張させて新しいメソッドを追加するときにできる階層関係
実装の階層
インターフェースの具体的な処理を書くときに追加されたクラスとできる階層関係
だいたいこんな風に理解したのだけど、説明はへたくそです。実際問題、本買った方が早いです。
本にのってたクラスはこんな感じ。
- Display.as
機能クラスの親分 - CountDisplay.as
機能クラスの兄弟分。Displayのバリエーション。 - DisplayImpl.as
実装クラスの親分。インターフェースだけある。 - StringDisplayImpl.as
実装クラスの子分。ほんとに作る仕事をしているのはこの人。 - Main.as
クライアント。依頼人。
// Display.as class Display{ private var impl:DisplayImpl; function Display(i:DisplayImpl){ impl = i; } function Open():String{ return impl.rawOpen(); } function Print():String{ return impl.rawPrint(); } function Close():String{ return impl.rawClose(); } function display():String{ var tmp = Open(); tmp = tmp + Print(); tmp = tmp + Close(); return tmp; } }
表示させる大まかな手順を書いたクラス。この前のTemplateMethodとだいたい同じ感じですね。だいぶ慣れて来た。機能クラスの大元にあるクラスです。で、コンストラクタで機能を実装するためのインスタンスを放り込むimplが実装への「橋」になる部分で今回のポイント。
// CountDisplay.as class CountDisplay extends Display{ function CountDisplay(i:DisplayImpl){ super(i); } function multiDisplay(times:Number){ var tmp = Open(); for(var i = 0 ; i>times ; i++){ tmp = tmp + Print(); } tmp = tmp + Close(); return tmp; } }
機能クラスの兄弟分すなわちバリエーションがこちら。表示させる回数を引数でうけてその分ループさせる機能が追加されている。実装は拡張されていない。
// DisplayImpl.as class DisplayImpl{ function rawOpen():String{return "";}; function rawPrint():String{return "";}; function rawClose():String{return "";}; }
実装の親分。インターフェース。べつに無くてもいいんじゃない?って気がしなくもない。
// StringDisplayImpl.as class StringDisplayImpl extends DisplayImpl{ private var _str:String; private var _w:Number; function StringDisplayImpl(s:String){ _str = s; _w = s.length; } function rawOpen():String{ return printLine(); } function rawPrint():String{ return "|" + _str + "|¥n"; } function rawClose():String{ return printLine(); } function printLine():String{ var tmp = "+"; for (var i = 0 ; i< _w ; i++){ tmp = tmp+"-"; } tmp = tmp + "+¥n"; return tmp; } }
実装の子分。下っ端なんでじっさいにテキストを生成したりの泥臭い仕事を担っている。
// Main.as class Main { function Main(path :MovieClip ) { var d1:Display = new Display(new StringDisplayImpl("Hello , Japan")); var d2:Display = new Display(new StringDisplayImpl("Hello , World")); var d3:CountDisplay = new CountDisplay(new StringDisplayImpl("Hello , Univers")); var tmp=d1.display(); tmp = tmp + d2.display(); tmp = tmp + d3.display(); tmp = tmp + d3.multiDisplay(5); // 表示 path.createTextField("tf", 1, 5, 5, 190, 200); var tfm:TextFormat = new TextFormat("Courier",10,0x000000,false,false,false); path.tf.setNewTextFormat(tfm); path.tf.border = true; path.tf.multiline= true; path.tf.wordWrap= true; path.tf.text = tmp; } static function main() { var t :Main= new Main(_root); } }
クライアント。
実装を変えたいときはDisplayクラスをnewするときに投げるインスタンスを変えるとさくっと変えられる。で、機能を変えたいときはDisplayの代わりにCountDisplayを使うみたいにするとさくっと変えられる。すべては「橋」のおかげですと。なるほどね。
ということで、次回。