2013年9月2日月曜日

1メソッドに何行まで書いていいか?

超久しぶりの投稿。
最近、ソースの品質についてばかり考える毎日。。。

典型的なオブジェクト指向を想定して”メソッド”ってことにしますが、
functionなりプロシージャなりに読み替えても同じです。

「1メソッドに何行まで書いていいか」はよく話題になりますよね。
場所によってはコーディング規約で〇行までって決まっていたり。
まあ、標準としては定量的に〇行って決まってたほうが分かりやすかったりしますが、
行数って本質じゃあないと思うんです。。。

要は、
メソッドが高凝縮になっている、
言い換えると、
「このメソッドが何をしているか」を"一言で"説明できるのであれば、
別に100行書こうが1000行書こうがいいと思うわけです。

じゃあ逆に、
凝縮度が低くて読むのも苦痛なスパゲッティコード(怒)ってどういうものなのか、
考えてみましょう。


  1. 変数がやたら多い
        変数っていうのは、
     メソッドの引数
     ローカル変数
     グローバル変数(そもそも最低限にしてくれ...)
         のトータルを意味しています。
    ※たまにクラスのフィールドをまるでグローバル変数のように使っている人がいますが、
      お願いなのでオブジェクト指向をちゃんと勉強してください。。。

        まあ、例えば

       
       public void execute(String arg1, String arg2, ...){//10個くらいの引数
            bool flag;
            String temp1;
            String temp2;
            ArrayList<String> arrA;
            ArrayList<String> arrB;

            //...ずらずらと10個以上変数宣言


            //そこから100行以上処理
      } 
   
    ...見た瞬間読む気失せるわ!!

      例外もあるにしても、
      引数が10個超えたり頭で10個以上ローカル変数を宣言したらその時点で設計を疑いましょう! 
      ましてや上記のように、
      arg1,arg2とか、arrA,arrBとか
    変数に枝番なんか付けだした日には最悪です!
      枝番付けるくらいなら速やかにprivateなサブメソッドを書いてくれ!!

  2. 変数の再定義が多すぎる
   
   いや、多すぎるっていうか、個人的には1回でも再定義が必要になったら
   疑うところだと思いますが。。。

   public void execute(){
            String t;

            if(something == true || condition == true){
                t = "0";
            }elseif(hoge == false){
                t = "1";
            }else{
                t = "2";
            }

            //...100行くらい処理
      
            t = "0"; //えっ...!?

            //全然違う条件で再定義.。。。
            if(hoge == true){
                t = "1";
            }else{
                t = "3"; //しかも3ってなんだよ。。。どっから出てきたんだよ。。。
            }

           //また100行くらい処理。。。

    }
   もう、敢えてセキュリティ上の理由かなんかで難読化してるとしか思えない。。。
   頼むからスコープの中でくらい変数の意味を統一してくれ!
   で、ハラが立って「再定義禁止!」とか言うと、今度は、
   t1,t2,t3とかが増殖し出す始末。。。

   きっと「メソッドを分離すると風邪を引く」みたいな迷信をおばあちゃんに教えられたに
   違いない。。。


 3. ループやtry-catchの中が長い

  冒頭で何行でも書いていいとは言いましたがこれは流石に。。。

  public void execute(){
        String[] arr1 = getArray1();
        String[] arr2 = getArray2();
        
        //まずforeachで書けないか疑ってくれ。。。
        for(int i = 0;i < arr1.length; i++;){
      try{
                   //for - try - for のネスト。。。
                   for(int j = 0; j < arr2.length; j++;){

           //100行くらい処理
           //こういうときに限ってさらにifのネストも深かったりする


                   }//ここを読む頃にはもうどこからループが始まったのやら。。。
             }catch(Exception e){
         //しかもException握り潰すんだ。。。
             }
        }
    }
  ここまでくると、書いた人の頭の中覗いて見たいレベル。。。
  このメソッドの処理を正しく理解するのは諦めるより他無いですな。。。

  はじめの{から終わりの}までが画面上ワンスクロール以内に終わらなかったら分割しましょう!
  ...だからと言って巨大な縦長ディスプレイ買ってもダメです。


結論として、上記のような条件に当てはまらなければ、
別に100行書こうが1000行書こうがいいと思いますよ。。。
そんな努力するくらいなら分割した方が早いけど。。。


2012年6月10日日曜日

javascriptとfunction型 その3

function型が第一級型ということは、functionを戻り値にすることもできます。
が、これの使い方は正直言ってややレベル高いです。。。
詳しく知りたい方は、「カリー化」「部分適用」「クロージャ」あたりの単語でgoogle先生に訊いてみると幸せになれるかも。

今回は、ブログのタイトルもタイトルなので、ちょっとメタルを絡めたコードを例を書いてみました。

//誰かを紹介するfunction...を返すfunction
function introduceSomeone(who){
   //ここでの戻り値はfunction
   return function(description){
      //上位の引数であるwhoも参照できるのがポイント
      //詳しくは「クロージャ」っていう単語でググって下さい。。。
      WScript.echo(who + " is " + description + ".");
   }
}


//"Alex"について紹介するfunctionが格納される
var introduceAlex = introduceSomeone("Alex");

//以下、Alexについての紹介
introduceAlex("a guiterist");// => "Alex is a guiterist."
introduceAlex("living in Finland");// => "Alex is living in Finland."
introduceAlex("a member of the heavy metal band called Children Of Bodom"); //=> "Alex is a member of the..."(略)

//こういう風に呼んでも結果は同じ
introduceSomeone("Alex")("the main composer of his band");// => "Alex is the main composer of his band."

以上、Alexのfunctionを返すfunctionの紹介でした。
要は、「複数の引数を取るけど、一回決めた引数は使いまわしたいfunction」に適用すると便利です。
毎回毎回引数を指定するのが面倒・・・というのもありますが、本筋は余計なバグを防止することじゃないでしょうか。
例えば、コピペをミスったりとか、他の処理から間違えて値を更新してしまったりとか。。。
上記の例のintroduceAlexを使っている限り、他の処理が何をやっていてもwhoが"Alex"であることが保障されるのがポイントです。
非同期とか分散処理をやっていても安全に使えるのがいいですね。

javascriptとfunction型 その2

functionが第一級型だということは、functionの引数にfunctionを渡すことも出来ます。

例えば,

function sayHello(name,_after) {
 WScript.echo("Hello," + name + "!");

  //_afterに渡されたfunctionを呼ぶ
  //ただ、_afterがあるかどうかもそれがfunctionかも分からないので
  //一応条件分岐
  if(_after && typeof(_after) == "function"){
     _after(name); //ここではnameで渡された値をそのままスルー
  }
}



//単にnameを元にechoするだけのfunction
function sayPleaseWelcome(name) {
 WScript.echo("Please welcome " + name + "!");
}

//こちらは_afterを指定してないので、単にsayHelloを実行
sayHello("Tetsuya"); //=>"Hello,Tetsuya!"

//_afterにsayPleaseWelcomeを指定しているので、
//sayHelloが呼ばれた後にsayPleaseWelcomeが呼ばれる
sayHello("Tetsuya",sayPleaseWelcome); //=> "Hello,Tetsuya!" => "Please welcome Tetsuya!"
のようにcallbackイベントを作るときなんかに使います。

この例ではsayHelloに渡されたnameをそのまま_afterに渡していますが、例えば最初のfunctionでなんらかの処理をした結果を_afterの引数として渡してあげるともっと使い勝手が良いです。
めんどくさいのでコードは書きませんが、ajaxなんかの非同期処理がバンバン出てくるときに使うと便利ですね。

ここでは、上のコードをもうちょっといじって遊んでみます。
例えば、私、Tetsuyaが嫌われている場合のソースコード。
他の人は歓迎しますが、Tetsuyaだけは嫌いなので出て行って欲しいのです。。。
でも、大人としてちゃんと挨拶だけはしてくれます。

//最初の挨拶
function sayHello(name,_after) {
  WScript.echo("Hello," + name + "!");
  if(_after && typeof(_after) == "function"){
      _after(name);
  }
}

//歓迎すべき人への対応
function sayPleaseWelcome(name) {
   WScript.echo("Please welcome " + name + "!");
}
//好ましく無い人への対応
function sayGetOut(name) {
   WScript.echo("Get out of here," + name + "!");
}

//最初の挨拶はするけど、その後の対応は人による
function userComing(name){
   if(name == "Tetsuya"){
      //Tetsuyaだけは出て行って欲しい
      sayHello(name,sayGetOut);
   }else{
      //他の人は歓迎
      sayHello(name,sayPleaseWelcome);
   }
}



//Tetsuyaが来た場合
userComing("Tetsuya");// => 「出て行け」ってゆわれる(涙)

//Taroが来た場合
userComing("Taro");// => 歓迎される(^ o ^) 

javascriptとfunction型 その1

FaceBookにも書きましたが、javascriptは「functionが第一級型」「すべてが連想配列」ということが腑に落ちるようになるとすごくおもしろくなります。 なので、今回は「functionが第一級型」についてちょっと解説。

第一級型ってなんぞやっていうのはWikipediaでも見てもらうとして、 要するに、functionもstringやboolと同じように、自由に変数・引数・戻り値として使えるということですね。

例えば、これはごく初歩的なfunctionの使い方

function sayHello(name) {
   return "Hello," + name + "!";
}

WScript.echo(sayHello("Tetsuya"));// => "Hello,Tetsuya!"

では、(あんまり意味ないけど)このsayHelloっていう関数を変数に代入してみます。

function sayHello(name) {
   return "Hello," + name + "!";
}

//WScript.echo(sayHello("Tetsuya"));// => "Hello,Tetsuya!"

var _say = sayHello; // _sayにsayHelloというfunctionを代入
WScript.echo(_say("Tetsuya")); // => "Hello,Tetsuya!"(上でコメントアウトした行とまったく同じ)

このようにfunction型も普通に変数に格納することが出来て、格納した変数に()をつけてあげることで呼び出すこともできます。

ちなみに、

function sayHello(name) {
   return "Hello," + name + "!";
}

WScript.echo(typeof(sayHello)); //=> "function"
WScript.echo(typeof(sayHello()));//=> "string"
このようにtypeofで型を調べると、
  • sayHello("()"を付けない)=>function型
  • sayHello()("()"を付ける)=>string型(関数が実行されてreturnで返した値が返る)
というのが分かりますね。


もちろん、上記の例はあんまり意味の無いコードなので、「だからどうした!?」って言いたい気持ちは分かります(笑)
でも、この特性をうまく使うと面白いことができるんです。続きは次回。

初ブログ

眠れないのでブログ初めてみた。
FaceBookとかだと入力できる行数が少ないので。。。長々と語るのが好きなオタクにはちょうどいいかと。

ちなみにタイトルは私が好きなもの、
メタル=パワーコード、プログラミング=ソースコード
を表しています。もちろん、chordとcodeをかけたダジャレでもあります。

話題はやっぱりそのふたつになるかな。。。

でも、なるべくプログラミング系の話題を書いて、
forkwellとかで褒められたい。。。