-
Notifications
You must be signed in to change notification settings - Fork 8
SplitBody
このエントリでは、JavaScript コードのヘッダとボディを分離する理由について述べます。
あなたのコードはどんな時に読まれるのでしょう?
- 80% の時間は「どんな機能があるか」を知るために使われます。
- 「どんな API があるのか」「どんな引数で何を返すのか」を知るために何度も何度もチラチラと見られます。
- 残り 20% の時間は「実装の確認のため」に読まれます。
- デバッグする場合はこちらです。
あなたのコードは API の確認のために読まれています。
また、あなたのコードを読む人はどんな人でしょう?
- デバッグのために読む人。時間に余裕が無く、イライラしているかもしれません、恐らくは無関係な部分は読み飛ばしたいと考えています。またどこを見ればよいのか瞬時に知りたいと考えています。
- 興味本位でコードを読む人。とりあえず上から下まで軽く読み流します。
- 評価のためにコードを読む人。数秒〜数分でコードの質とコーディングセンスの良し/悪しも同時に読まれています。
アフタヌーンティー片手に、コードを読んでくれる人はかなり少なそうです。
つまり、コードを読む人の殆どは「API を素早く確認したい」のです。
このような期待に応える術をもたない人や、そもそも読み手の行動をイメージできない人は、API を集中的に記載したヘッダ部分を作らず、実装(ボディ)にAPIの定義を混ぜ込んだ練度の低いごちゃごちゃとしたコードを書きます。
このようなコードは、時間当たりに得られる情報量が低いと考えられるため、多忙な人からは「あまり読みたくないコード」「汚いコード」と判断されてしまう事になります。
コードの読み手が、あなたの転職希望先の人事担当者 + 多忙を極める現場の偉い人(2〜3次面接の面接官)だった場合は、どうなるでしょうか?
最も良い方法は、コードの先頭部分に API の一覧を記載することです。
コードの先頭部分を一瞥すれば 何ができるか を判別できる状態にします。
これらは特別な新しいアイデアではなく、C/C++ のヘッダファイルの役割そのものです。
こちらは、ヘッダ部分にも API の一覧がなく、またコードの追加でどんどん見通しが悪くなるため、読み手の時間を無駄に奪ってしまう悪い例です。
何をするにもスクロールが必要で、コードの追加に伴い見通しもどんどん悪くなる肥満体質(pyknic type)なコードと言えます。
(function(window, undefined) {
var MyExample = function() {
...
};
MyExample.prototype.get = function(arg1) {
...
};
MyExample.prototype.set = function(arg1) {
...
};
MyExample.prototype.clear = function(arg1) {
...
};
MyExample.prototype.reset = function(arg1) {
...
};
...
MyExample.prototype.move = function(arg1, arg2, arg3) {
...
};
MyExample.prototype.copy = function(arg1, arg2, arg3) {
...
};
MyExample.prototype.make = function(arg1, arg2, arg3) {
...
};
MyExample.prototype.remove = function(arg1, arg2) {
...
};
window.MyExample = MyExample;
})(window);
こちらは、コードの先頭部分(ファーストビュー)に API の一覧を集約したコードです。
殆どスクロールせずに、どんな機能が有るかを素早く把握できます。
関数の本体はヘッダの下に書きます。
GLOBAL["WebModule"]["exports"]("MyExample", function(global) {
"use strict";
// --- dependency modules ----------------------------------
// --- define / local variables ----------------------------
// --- class / interfaces ----------------------------------
function MyExample() {
...
}
MyExample["prototype"] = Object.create(MyExample, {
"constructor": { "value": MyExample }, // new MyExample():MyExample
"get": { "value": MyExample_get }, // MyExample#get(arg1:Any):this
"set": { "value": MyExample_set }, // MyExample#set(arg1:Any):this
"clear": { "value": MyExample_clear }, // MyExample#clear(arg1:Any):this
"reset": { "value": MyExample_reset }, // MyExample#reset(arg1:Any):this
:
"move": { "value": MyExample_move }, // MyExample#move(arg1:Any, arg2:Boolean, arg3:String):this
"copy": { "value": MyExample_copy }, // MyExample#copy(arg1:Any, arg2:Boolean, arg3:String):this
"make": { "value": MyExample_make }, // MyExample#make(arg1:Any, arg2:Boolean, arg3:String):this
"remove": { "value": MyExample_remove } // MyExample#remove(arg1:Any, arg2:Number):this
});
// --- implements ------------------------------------------
function MyExample_get(arg1) {
...
}
function MyExample_set(arg1) {
...
}
function MyExample_clear(arg1) {
...
}
function MyExample_reset(arg1) {
...
}
...
function MyExample_move(arg1, arg2, arg3) {
...
}
function MyExample_copy(arg1, arg2, arg3) {
...
}
function MyExample_make(arg1, arg2, arg3) {
...
}
function MyExample_remove(arg1, arg2) {
...
}
return MyExample; // return entity
});