-
Notifications
You must be signed in to change notification settings - Fork 8
SplitBody
uupaa edited this page Oct 13, 2014
·
30 revisions
このエントリでは、JavaScript コードのヘッダとボディを分離する理由について述べます。
あなたのコードはどんな時に読まれるのでしょう?
- 80% の時間は「どんな機能があるか」を知るために使われます。
- 「どんな API があるのか」「どんな引数で何を返すのか」を知るために何度も何度もチラチラと見られます。
- 残り 20% の時間は「実装の確認のため」に読まれます。
- デバッグする場合はこちらです。
あなたのコードは API の確認のために読まれています。
また、あなたのコードを読む人はどんな人でしょう?
- デバッグのために読む人。時間に余裕が無く、イライラしているかもしれません、恐らくは無関係な部分は読み飛ばしたいと考えています。またどこを見ればよいのか瞬時に知りたいと考えています。
- 興味本位でコードを読む人。とりあえず上から下まで軽く読み流します。
- 評価のためにコードを読む人。数秒〜数分でコードの質とコーディングセンスの良し/悪しも同時に読まれています。
コードを読む人の殆どは「API を素早く確認したい」のです。
このような期待に応える術をもたない人や、そもそも読み手の行動をイメージできない人は、API を集中的に記載したヘッダ部分を作らず、実装(ボディ)にAPIの定義を混ぜ込んだ練度の低いごちゃごちゃとしたコードを書きます。
このようなコードは、時間当たりに得られる情報量が低いと考えられるため、多忙な人からは「あまり読みたくないコード」「汚いコード」と判断されてしまう事になります。
コードの読み手が、転職希望先の多忙を極める面接官だった場合は、どうなるでしょうか?
最も良い方法は、
コードの先頭部分に API の一覧を記載することです(in JavaScript)。
コードのヘッダを一瞥すれば、そのコードに実装されている 60〜80% の情報を得られる状態にします。
これらは特別な新しいアイデアではなく、C/C++ のヘッダファイルの役割そのものです。
こちらは、ヘッダ部分にも API の一覧がなく、またコードの追加でどんどん見通しが悪くなる(ファットになる)ため、読み手の無駄に時間を奪ってしまうダメな実装例です。
何をするにもスクロールが必要です。
(function(window, undefined) {
var MyClass = function() {
};
MyClass.prototype.get = function(arg1) {
...
};
MyClass.prototype.set = function(arg1) {
...
};
MyClass.prototype.clear = function(arg1) {
...
};
MyClass.prototype.reset = function(arg1) {
...
};
...
MyClass.prototype.move = function(arg1, arg2, arg3) {
...
};
MyClass.prototype.copy = function(arg1, arg2, arg3) {
...
};
MyClass.prototype.make = function(arg1, arg2, arg3) {
...
};
MyClass.prototype.remove = function(arg1, arg2) {
...
};
window.MyClass = MyClass;
})(window);こちらは、コードの先頭部分に API の一覧を集約したコードです。
スクロールせずに、どんな機能が有るかを一目で把握できます。
(function(global) {
// --- class / interfaces ----------------------------------
function MyClass() {
}
MyClass.prototype = {
constructor: MyClass, // new MyClass():MyClass
get: MyClass_get, // MyClass#get(arg1:Any):this
set: MyClass_set, // MyClass#set(arg1:Any):this
clear: MyClass_clear, // MyClass#clear(arg1:Any):this
reset: MyClass_reset, // MyClass#reset(arg1:Any):this
:
move: MyClass_move, // MyClass#move(arg1:Any, arg2:Boolean, arg3:String):this
copy: MyClass_copy, // MyClass#copy(arg1:Any, arg2:Boolean, arg3:String):this
make: MyClass_make, // MyClass#make(arg1:Any, arg2:Boolean, arg3:String):this
remove: MyClass_remove // MyClass#remove(arg1:Any, arg2:Number):this
};
// --- implements ------------------------------------------
function MyClass_get(arg1) {
...
}
function MyClass_set(arg1) {
...
}
function MyClass_clear(arg1) {
...
}
function MyClass_reset(arg1) {
...
}
...
function MyClass_move(arg1, arg2, arg3) {
...
}
function MyClass_copy(arg1, arg2, arg3) {
...
}
function MyClass_make(arg1, arg2, arg3) {
...
}
function MyClass_remove(arg1, arg2) {
...
}
// --- exports ---------------------------------------------
global["MyClass"] = MyClass;
})((this || 0).self || global);