いきなり更新頻度が落ちて喫茶マウンテンの形相を帯びてきたこのコーナーですが、まだ続けてます。ぜひ登頂したいところです。
今回のお題はPrototype(コピーしてインスタンスを作る)です。今回のパターンの意義として以下の3つがテキストに書いてありました。
1)似たようなクラスが大量にできてしまいそうな場合
2)複雑な過程を経てインスタンス化される場合
3)フレームワークとインスタンスを切り分けたい場合
クラス→インスタンス じゃなく、インスタンス→インスタンスというふうに生成するとコードがすっきりするし、クラス名がソースコードに書かれないので、部品としての結合が弱まって再利用しやすくなるらしいです。なるほど。と、いわれても、いまいちピンとこないです。まあ、とりあえず形を覚えて使えそうな場面をこれから意識的に探してみよう。
で、ここで問題になったのはインスタンスのコピーでした。Javaとかだと、Cloneableをimplementsして一発だね、と言った感じです。(使ったことないけど、ドキュメントは読んだことあります。)
あれこれ悩んで、ちょっとhatenaのnaoya氏のコードをカンニング。cloneメソッドを下のような感じで自前で実装してました。
var p = new Object(); for (var i in this ){ p[i] = this[i]; } return p;
基本的にはfor in でサーチして個々の要素をオブジェクトに放り込み、できたものを戻すと。なるほどなるほど。
で、早速実装してみたけど、動かぬ。
orz
なぜだー。あちこちtraceしてみると、メンバ変数はコピーされてるのだけど、メソッドが置いてきぼりをくってるっぽい。shallow copy(浅いコピー)というやつなのかな?
なかばやけくそ気味に、
var p = new Object(); p = this; return p;
としたら、来た。へ?これだけ?どのレベルまでコピーされてるのか後でちょっと調べる必要があるけど、今のところこれ採用で。
以下ソースコード。
// framework/Product.as interface framework.Product{ function use(s:String):String; function createClone():Object; }
frameworkパッケージ内のインターフェースです。インターフェースもだいぶ慣れてきた。
// framework/Manager.as import framework.*;class framework.Manager{
var showCase:Object;
function Manager(){
showCase=new Object();
}
function register(name:String, proto:Product){
showCase[name] = proto;
}
function create(protoname:String):Product{
var p = showCase[protoname];
return p.createClone();
}
}
インスタンスの複製を行うクラス。実体はサブクラスで実装。createメソッド以下はまとめられるかなと思ったのだけど、とりあえずこのままで。クラスを名前でObjectに登録し、コピーするときは登録した名前からひっぱってくる。こうすると名前をソースコードに記述しなくてよい。これが今回のポイントです。
// MessageBox.as import framework.*;class MessageBox implements Product{
private var decochar:String;
function MessageBox(d:String){
decochar = d;
}
function use(s:String):String{
var Length:Number = s.length;
var tmp = "";
for(var i=0 ; i < Length+4 ; i++){
tmp=tmp+decochar;
}
tmp=tmp+"¥n";
tmp=tmp+decochar+" "+s+" "+decochar+"¥n";
for(var i=0 ; i < Length+4 ; i++){
tmp=tmp+decochar;
}
tmp=tmp+"¥n";
return tmp;
}
function createClone():Object{
var p = new Object();
/*for (var i in this ){
p[i] = this[i];
}*/ //と書いたらメンバ変数だけコピーされた
p = this;
return p;
}
}
具体的なサブクラス1でした。
// UnderlinePen.as import framework.*;class UnderlinePen implements Product{
private var ulchar:String;
function UnderlinePen(u:String){
ulchar = u;
}
function use(s:String):String{
var Length:Number = s.length;
var tmp = s+"¥n";
for(var i=0 ; i < Length+4 ; i++){
tmp=tmp+ulchar;
}
tmp=tmp+"¥n";
return tmp;
}
function createClone():Object{
var p = new Object();
/*for (var i in this ){
p[i] = this[i];
}*/ //と書いたらメンバ変数だけコピーされた
p = this;
return p;
}
}
具体的なサブクラス2です。
どちらもcreateCloneメソッドで自分自身をコピーしてます。Javaだと、自分自身からしかcloneメソッドを呼べないのでラッピングしなきゃならんそうですが、ActionScriptだと呼ぶも何も、代入してるだけだしなあ。これでよいのか?昔ながらのいい加減さを醸し出してるように思えた。あ、だから抽象クラスでいっそのことcreateCloneメソッドをまとめちゃえばいっかなと思ったのか。
//Main.as import framework.*;class Main{
//メインクラスfunction Main(path :MovieClip ) {
// 準備
var manager:Manager = new Manager();
var upen:UnderlinePen = new UnderlinePen("~");
var mbox:MessageBox = new MessageBox("*");
var sbox:MessageBox = new MessageBox("/");
manager.register("strong message" , upen);
manager.register("warnign box" , mbox);
manager.register("slash box" , sbox);
var p1= manager.create("strong message");
var tmp=p1.use("hello world");
var p2 = manager.create("warnign box");
tmp=tmp+p2.use("hello world");
var p3= manager.create("slash box");
tmp=tmp+p3.use("hello world");
// 表示
path.createTextField("tf", 1, 5, 5, 190, 190);
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);
}
}
ひらすら、newしたインスタンスをmanagerインスタンスに名前登録して実際メソッドを使うときは登録した名前から引っ張ってきて使う。ほほう。
文字の崩れが気になってきたので、いつものtextFieldに等幅フォントを指定。
で、
mtasc -swf prototype.swf -main Main.as -header 200:200:30
で動きます。
いままで物珍しかったので、耳かきエディタ+FlashMXモード+MTASCでコード書いてたのだけど、ちょっと今回ので厳しくなってきた感が出てきた。とくにtraceがだめなのはつらい。そろそろ、おれも、eclipse+FDT+MTASCに移行しようかな。199ユーロかあ。