- Notifications
You must be signed in to change notification settings - Fork8
SplitBody
このエントリでは、JavaScript コードのヘッダとボディを分離する理由について述べます。
あなたのコードはどんな時に読まれるのでしょう?
- 80% の時間は「どんな機能があるか」を知るために使われます。
- 「どんな API があるのか」「どんな引数で何を返すのか」を知るために何度も何度もチラチラと見られます。
- 残り 20% の時間は「実装の確認のため」に読まれます。
- デバッグする場合はこちらです。
あなたのコードは API の確認のために読まれています。
また、あなたのコードを読む人はどんな人でしょう?
- デバッグのために読む人。時間に余裕が無く、イライラしているかもしれません、恐らくは無関係な部分は読み飛ばしたいと考えています。またどこを見ればよいのか瞬時に知りたいと考えています。
- 興味本位でコードを読む人。とりあえず上から下まで軽く読み流します。
- 評価のためにコードを読む人。数秒〜数分でコードの質とコーディングセンスの良し/悪しも同時に読まれています。
アフタヌーンティー片手に、コードを読んでくれる人はかなり少なそうです。
つまり、コードを読む人の殆どは「API を素早く確認したい」のです。
このような期待に応える術をもたない人や、そもそも読み手の行動をイメージできない人は、API を集中的に記載したヘッダ部分を作らず、実装(ボディ)にAPIの定義を混ぜ込んだ練度の低いごちゃごちゃとしたコードを書きます。
このようなコードは、時間当たりに得られる情報量が低いと考えられるため、多忙な人からは「あまり読みたくないコード」「汚いコード」と判断されてしまう事になります。
コードの読み手が、あなたの転職希望先の人事担当者 + 多忙を極める現場の偉い人(2〜3次面接の面接官)だった場合は、どうなるでしょうか?
最も良い方法は、コードの先頭部分に API の一覧を記載することです。
コードの先頭部分を一瞥すれば何ができるか を判別できる状態にします。
これらは特別な新しいアイデアではなく、C/C++ のヘッダファイルの役割そのものです。
こちらは、ヘッダ部分にも API の一覧がなく、またコードの追加でどんどん見通しが悪くなるため、読み手の時間を無駄に奪ってしまう悪い例です。
何をするにもスクロールが必要で、コードの追加に伴い見通しもどんどん悪くなる肥満体質(pyknic type)なコードと言えます。
(function(window,undefined){varMyExample=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 ----------------------------------functionMyExample(){ ...}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 ------------------------------------------functionMyExample_get(arg1){ ...}functionMyExample_set(arg1){ ...}functionMyExample_clear(arg1){ ...}functionMyExample_reset(arg1){ ...}...functionMyExample_move(arg1,arg2,arg3){ ...}functionMyExample_copy(arg1,arg2,arg3){ ...}functionMyExample_make(arg1,arg2,arg3){ ...}functionMyExample_remove(arg1,arg2){ ...}returnMyExample;// return entity});