ソフトウェア開発の現場では、いやおうなく人の書いたプログラムを追いかけなくてはならないことがあります。その時、自分とは違うスタイルのプログラミングを目にすることになります。
その中でよく見かけるスタイルの一つが、「引数も戻り値もない関数」です。また、その変形として、「引数がない関数」、「戻り値がない関数」があります。これらのスタイルは、関数の中で、どこからか値を引っ張り出し、計算をして、どこかへ値をしまうというものです。
とても単純化したモデルでこのコードを書いてみます。
class Hoge { Huga fieldA; int fieldB; Bafu fieldC; public void foo() { int inA = fieldA.getValue(); // 他のインスタンスから値を取得 long out = inA + fieldB; // 計算 fieldC.setValue(out); // 他のインスタンスに値をセット } }
実際には、もっと複雑なので、ぱっとコードを見ただけでは問題意識を感じずに済みます。しかし、そのコードにアスペクト指向的にログを仕掛けようとしたり、ユニットテストを加えてみようとしたときに、問題が浮き彫りになります。
- Aspect指向ツールでfooメソッドが呼ばれたときに、計算の入出力をログに出そうとしても、引数、戻り値がないので、単純には値を出すことができない
- ユニットテストを書こうとしても、fooのテストケースを書くことは難しい。この場合は、Hogeクラスのテストを書くのに、Huga、Bafuが必要になる、またはHuga、Bafuのモックを作らなくてはならない
- エラー時、どのような例外がスローされるのか、特定が難しい(Huga、Bafuの設計に左右される)
往々にして、このようなコードを書く人は、テストもログも出しておらず、おそらくテストはプログラム全体を動かしてなんとなく動いているからよしとしてしまっているか、デバッガ上であるケースを流して動いているからよしとしまっていると思います。
オブジェクト指向プログラミング以前は、このようなコードを書く人は、グローバル変数をたくさん定義して、そこから値を読み取り、値を書いていたのでしょう。オブジェクト指向になって、「グローバル変数は悪」と教わっても、「クラスのフィールド変数を読み書きしているから自分はOK」と思っていることでしょう。
このようなコードが書かれるのは、関数仕様を決めるのが面倒で、とりあえず引数・戻り値なしで取り掛かり、処理を書きながら、外部の値を参照する必要がでてきたらそのときに泥縄的に値を読み書きしながら作るからと思われます。段取りなしに作業に取り掛かり、課題に出くわしたらアドホックに対処していくという作業スタイルが身についてしまっているのでしょう。
最初から完璧な関数仕様が決められることはないですが、課題に出くわしたときに、一度原点に立ち戻って関数仕様を見直せば、よいプログラムになっていくのですが、その手間を惜しんで結果貧弱なコードになっていると思います。