単純化&最適化&DRY
問題の局所化およびDRYによるメンテナンスビリティを考えられないプログラマがいるとは、驚きに値する。
人間がミスをおかしにくくするためには、問題領域をできるだけ狭めて、何事にも単純化を行う必要があると僕は思っている。そして、一旦でき上がったシステムを長期的にメンテナンスしていくためには、昔流行った「各コンポーネントがそれぞれ能動的に何かをする(そのためには同一処理が複数箇所で記述されることを許容する)」のではなく、DRY(Don’t repeart yourself)の精神に従った最適化ロジックが適している、と10年以上システム開発に携わってきた経験から学んでいる。さらに、ある部分の実装コードを見て初めて何が行われているかが判断されるコーディングではなく、予め処理される情報が明確に特定可能な外部インタフェースを設計することが、メンテナンスビリティの向上につながると考えている。
たとえば、あるオブジェクトに情報を渡して処理をしてもらう際にも、
Map m = …; Hoge h = new Hoge(m) { public void foo() { List l = m.get(“bar”); // l に対する処理 } };
ではなく、
Map m = …; Hoge h = new Hoge(m.get(“bar”)) { public void foo() { // l に対する処理 } };
と記述してあげるのが正解(Hogeクラスの中でキーが”bar”以外のm内の情報が使われることはない、という前提)だと考える。なぜなら、
-
Hogeクラスの中で対象となる問題領域(ここではm.get(“bar”)の戻り値)を限定できている。
-
Hogeクラスから見て「余計な情報」が多いmそのものを渡すよりも、処理対象のみのm.get(“bar”)を渡すほうが問題領域の局所化が実現できている。
-
↑が外部インタフェースから読み取ることができる(データ構造は把握している前提)。
と考えるからだ。
これに対して、Hogeクラスのみを見れば、何をしているのかが全てわかる、という前者のコードについても、確かにわからなくもない考え方である。しかし、m.get(…)に渡す引数を予めHogeクラスのインスタンスが知っていないといけない、などの点を考えると、余計な引数がコンストラクタに必要になる懸念も考えられるため、結果として外部インタフェースが複雑になってしまう危険性もある。
さらに、Hogeクラスに対する単体テストを考えると、本来m.get(“bar”)で得られる情報のみを事前準備すればいいだけなのに、事前情報をより複雑かつ本質的ではないデータ構造で準備しなければならなくなる。この単体テストの複雑化は、外部インタフェースの質が悪いことを意味する。
さらに、上記はJavaのコードであるが、これがWebブラウザ上で実行されるJavaScriptとなると、余計に単純化&最適化&DRYを考えなければならない。ま、別にどこで実行されようとも一緒だけど。そして、単純化&最適化&DRYを進めた結果コーディングが汚くなる、なんてことはなく、むしろきれいになる。どこのコードを追っても対象となる問題領域は狭い、という状態が、結果として把握しやすいコードになるはずである。
・・・久々にコーディングの方針で身内と議論になったので、僕の考えをここに表明してみた。ま、気持ち任せで書いた文章なので、説明不足な箇所もあるかもしれないので、その辺はご容赦を。