Skip to content
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);

Clone this wiki locally