2013年04月19日

Javascriptで素敵なオブジェクト指向プログラミングライフを提供するがおクラスについて

おはようございます。

Googleが提供している「google-code-prettify」というシンタックスハイライターを導入したら、細かい仕様が気になりまくってcssやらjsやらいじりまくってたら朝チュンしましたけど私は元気です()

さて、今日は「がおクラス」について話そうと思います。
知ってる人は知っているこの「がおクラス」、実はしっかりまとめている文書がひとつもないという致命的な欠陥を抱えてることが発覚したので、今回この記事を投稿しました。

ものすごくものすごく頑張ったシンタックスハイライターをフルに使って解説したいと思うので、よろしくおねがいします。(何を)さて、そもそも「がおクラス」とは?ってところから説明しましょうか。

がおクラスとは、その名の通り僕が考えたJavascriptのオブジェクト指向なクラスを記述する記法(またはその記法に基づいて記述されたクラス)のことを言います。

Javascriptでクラスとかいう用語を出すと、「いやそれ主に関数オブジェクトやプロトタイプチェーンを利用したクラスっぽいものだからクラスじゃねーですよ」っていうガチこわい人からのツッコミが飛んでくるわけですが、

まあ、便宜上、今回はクラスと呼びましょう(許してください)
(ちなみにCSS&HTMLの.classとは一切関係ないですよ!><)

で、まあどういう記法なのか、というのは、がおクラスのテンプレートを用意したのでこちらをみてみましょう。

function TestClass(){
var public = this;
var private = {};

public.publicInstanceVar;
private.privateInstanceVar;

public.PublicInstanceMethod = function(){
var localVar;
};
private.PrivateInstanceMethod = function(){
};
};

TestClass.CLASS_VAR;
TestClass.StaticMethod = function(){
};

はい、これが大まかな宣言一覧です。
クラス名は一番外側の関数の名前、TestClassです。UpperCamelCaseで記述しましょう。
そして2行目、3行目の

var public = this;
var private = {};

これは思考停止でそういうものを書くのだと思ってください()
この書き方こそ、がおクラスの要であり、Javascriptの気持ち悪さを全面に出している2行なのですが、実用上は全く気にする必要がないので、どうしても気になるーって方はJavascriptのスコープの話とか、this参照についてがしがしググってたら、多分わかるようになると思います(投げた)

変数は大きく分けて、クラス変数、インスタンス変数[public/private]、ローカル変数の計3種類(4種類とも)から構成されます。

クラス変数は、クラス間で共有して使用する定数を入れて使うのが一般的だと思います。
この変数の最大の特徴は、やはり「関数の中に書かずに直後に書く」ということですね。これはJavascriptの仕様で・・・というところが大きく、止むえず外側に記述しているという面があります。しかたないね
記述方法は「クラス名.SNAKE_CASE」で書くようにしましょう。大文字のほうが定数っぽいので大文字超推奨。
インスタンス変数はインスタンス内で共有して使用する変数です。
Javascriptとがおクラスの関係上、インスタンス変数は「宣言も代入もクラス内のどこでもできちゃう」という割と大きな問題があります
あっちこっちで宣言をすると、どの変数を宣言したっけ?っていう状況になりかねないので、クラス変数の直後に宣言だけ列挙して、代入は必ず各メソッド内で行うようにしましょう(ユーザ側が意識して、ね)
なお、記述方法は「public.lowerCamelCaseもしくはprivate.lowerCamelCase」で記述しましょう。publicで宣言したものに関しては、インスタンス外からも「インスタンス名.publicVar」という形で呼び出すことができます。一方で、privateで宣言した場合には、インスタンス内からのみ呼び出すことができます。

最後にローカル変数ですが、これは特定のメソッド内のみで使用する変数です。
for文のループ変数や、ちょっとしたエイリアス(別名参照)として利用するといいでしょう。
記述方法は「var lowerCamelCase;」です。一般的なJSの変数宣言と全く同じですので、迷わないと思います。


(わりと語った気がしますが)まだメソッドが残ってますね。ここら辺はさっさと済ませましょう。
メソッドにはインスタンスメソッドとして[private,public]の2種類があり、更にスタティックメソッドもあります。
インスタンスメソッドの利用方法はインスタンス変数とほぼ一緒です。
また、記述方法は「public.UpperCameCaseまたはprivate.UpperCameCase」です。
ここまできてお気づきかと思いますが、命名規則にはかなり敏感になって語ってきました。その理由がまさにここにあるといっても過言ではありません。
というのも、「がおクラス」の特徴として、インスタンス変数とインスタンスメソッドは同じ「public」もしくは「private」のオブジェクトの中に格納していきます。
ここで、命名をキチンとしていなかったら、インスタンス変数とインスタンスメソッドの名前が競合して、意図しないオーバーロードを引き起こす可能性があります。なので、確実に命名規則を守るようにしましょう。

スタティックメソッドは、クラス変数と同様に、クラスの外側に記述します。これもやはりJS側の仕様の問題です。
スタティックメソッドには、クラスで共有するメソッドを記述します。このメソッドにはインスタンス変数を参照する能力が無いので、クラス名の名前空間を借りているだけの全く別スコープの関数、といった感覚で記述できます。
(とはいえ上記のそれ以外のものに比べて圧倒的に使用頻度が少ない地味子ではあります)

インスタンスの精製方法は、new演算子で呼び出すだけのシンプルな設計です。

var testInstance = new TestClass();

これだけで呼び出すことができます。


さて、これでひと通りのパーツについては説明したので、突っ込まれそうな質問に対して予め答えましょう
Q.継承とは
A.ない(なんとかしましょう、因みに僕が何個か書いてきたものでは、必要になった場面は1つもありませんでした)
Q.インスタンスメソッド周りの名前冗長じゃね?
A.この記法は見やすさにもかなり神経質になっている部分があり、呼び出すごとにprivateだのpublicだの書かされるのは、その変数のスコープが一発でわかるので、むしろ便利だからこれで。
どうしても気に入らなかったら、2行目と3行目の重要宣言部分で、名前を短いものに変えたら短縮はできます。


最後に、ちょっとしたサンプルを見せて終わりましょうか。

function SindanClass(){
var public = this;
var private = {};

public.userName;
private.title;
private.info;
private.resultList;

private.Init = function(arg){
if(arg[0] && arg[0].userName){
public.userName = arg[0].userName;
}
if(arg[0] && arg[0].title){
private.title = arg[0].title;
}
else{
SindanClass.Error("Init","titleは必ずインスタンス生成時の引数に含めてください");
}
if(arg[0] && arg[0].info){
private.info = arg[0].info;
}
else{
SindanClass.Error("Init","infoは必ずインスタンス生成時の引数に含めてください");
}
if(arg[0] && arg[0].resultList){
private.resultList = arg[0].resultList;
}
else{
SindanClass.Error("Init","resultListは必ずインスタンス生成時の引数に含めてください");
}
};


public.StartTodaySindan = function(){
return private.title + "\n" + private.info;
};
public.GetTodayResult = function(){
var res = private.GetRandomSeed();
var list = private.resultList;
return list[res%list.length];
};
private.GetRandomSeed = function(){
public.userName;
if(!public.userName){
SindanClass.Error("GetRandomSeed","ユーザー名をpublic.userNameで指定してください。");
}
var seed = SindanClass.TODAYDATE;
for(var i=0,iLen=public.userName.length;i<iLen;++i){
seed += public.userName.charCodeAt(i);
}
return seed;
};

private.Init(arguments);
};
SindanClass.TODAYDATE = (new Date().getFullYear()+new Date().getMonth()+new Date().getDate());
SindanClass.Error = function(method,message){
throw new Error("SindanClass." + method + " : " + message);
};

var sindanArg = {
title : "えいえんの○○歳ったー",
info : "君は若くあり続けられるだろうか",
resultList : [0,2,4,17,18,20,25,30,55,78,99]
};
var sindanIns = new SindanClass(sindanArg);
window.alert(sindanIns.StartTodaySindan());
sindanIns.userName = window.prompt("ユーザ名を入力してください");
var res = sindanIns.GetTodayResult();
window.alert(sindanIns.Name + "は" + res + "歳です。" + ((res<20) ? "若いですね" : "(お察し)"));


これはTwitter界では有名な診断メーカーもどきを、「がおクラス」で実装した例です。
全機能を有効に利用するために頑張りました。この例を考えるのに半日かけたのは内緒。

コメントとか一切書いてないけど、わかるよね?今まで説明してきたことの総復習みたいな感じです。
そして、このソースコードを読んでみてください。
読みやすいな〜って実感してもらえたら、がおクラスの強力さが分かると思います。(どう捉えるかはあなた次第です)
posted by がお at 07:37| Comment(0) | Javascript | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。