第7章:いつまで経っても変更作業が終わりません

はじめに

ソースコードの変更には時間がかかる。

  • 理解しにくいゴチャゴチャした構成になっている。
  • 「遅延時間」によりフィードバックを得るのに時間がかかる。

依存関係を排除してビルドの時間を短くし、フィードバックを早く得るための手法の紹介。
(依存関係を排除することで理解もしやすくなるだろうが、どちらかというと後者の「時間」という側面の記載のウェイトが多い印象)


「理解」という側面

何か変更を入れる必要が出たとして、変更に対する方法を見つけるにはある程度時間がかかる。
よくメンテされているコードに修正を入れるにしても、レガシーコードに修正を入れるにしても。

が、そこさえ分かれば、そのあとは前者を使う方が格段に速い。

  • 変更自体は簡単。
  • (書籍には書かれていないが)その変更が既存の動作に悪影響を及ぼしていないかの結果が分かる、という面もあると思っている。

後者の場合はそうはいかない。

  • 何をすべきか理解がかかるし、相手がレガシーコードなので変更も難しい。
  • 変更のために理解すべき範囲のカバーが十分にできない。


「小さく」「理解しやすく」「適切な名前の付いたパーツに分割された」システムでは、より速い作業が可能。
もし、そのプロジェクトで「コードの理解」という側面で問題があるのであれば、16章や17章が参考になるとのこと(本稿では割愛。というか、まだ読んでない)。

「時間」という側面

遅延時間
変更を行ってから、その結果を得るまでに経過する時間のこと。

インタプリタ言語(PerlとかRubyとか)でプログラミングをしている人は仕事の際にフィードバックをすぐに得ることができる。
が、コンパイル型言語で仕事をしている人にとってはそうはいかない。
ネックになるのは「コンパイルしたいもののために、関係のない部分までコンパイルしなければならなくなる依存関係」。


依存関係の排除
ビルドを簡単にするために、コードの構成を変更する方法の紹介。

例: 以下のように構成されるコードがあり、かつ、AddOpportunityFormHandlerに変更を加えたい。
ここで注目するのは、「①修正対象のAddOpportunityFormHandler」および「②(図には書かれていないが)AddOpportunityFormHandlerに依存しているクラス」の2つ。

修正前のコード構成
修正前のコード構成

着目点①:修正対象のAddOpportunityFormHandler
AddOpportunityFormHandlerのインスタンス化するときの問題点は以下の2つ。

  • 依存しているクラスはすべて具象クラス。
  • AddOpportunityFormHandlerをインスタンス化するときに芋づる式にいくつのクラスが呼び出されるのかは誰にもわからない。

これらの問題は、以下のようにすることで解決できる。

STEP1:ConsultantSchedulerDBへの依存を具象クラスではなくインタフェースに変更
STEP1:依存先を具象クラスからインタフェースに変更
STEP1:依存先を具象クラスからインタフェースに変更

  • ConsultantSchedulerDBインタフェースを実装した擬装オブジェクトでAddOpportunityFormHandlerオブジェクトを生成できる。
  • ConsultantSchedulerDBImplをいくら修正しても、AddOpportunityFormHandlerを再コンパイルする必要が無い。

⇒AddOpportunityFormHandlerはConsultantSchedulerDBImplに直接依存していないから。

⇒ AddOpportunityFormHandlerを再コンパイルする必要が出てくるのは、ConsultantSchedulerDBインタフェースに変更が入ったとき。

STEP2:OpportunityItemへの依存も具象クラスではなくインタフェースに変更
STEP2:依存先を具象クラスからインタフェースに変更
STEP2:依存先を具象クラスからインタフェースに変更

  • DB接続を責務にもつConsultantSchedulerDB(の実装クラス)とそれによって生成されるOpportunityItem(の実装クラス)への直接の依存がなくなることで、それらをいくら変更してもAddOpportunityFormHandlerを再コンパイルする必要が無くなる。

STEP3:パッケージ構造の変更
STEP3:パッケージ構成の変更
STEP3:パッケージ構成の変更

  • 1つのパッケージにまとめて入っていたクラスたちを、アプリケーション構造で明示的に表現する。


着目点②:修正対象のAddOpportunityFormHandlerを使うクラス
修正対象であるAddOpportunityFormHandlerを使うクラスの立場に立ってみると、以下のような解決すべき課題がある。

  • AddOpportunityFormHandlerに依存するコードのビルドも速くしたい。
  • AddOpportunityFormHandlerを修正するたびに再コンパイルしたくない。

これらの課題は、以下のようにすることで解決可能。

着目点②の課題を解決するためのコード構成
着目点②の課題を解決するためのコード構成
変更点は以下の2点。

  1. AddOpportunityFormHandlerを、何かしらのインタフェースの実装クラスにする。
  2. AddOpportunityFormHandlerに依存しているクラスは、そのインタフェースに依存するようにする。


依存関係の排除&コード構成を変更するメリット/デメリット
<メリット>
ビルド時間を早くすることができる。

  • 再ビルドとテストを素早く行うことができるようになると、開発の際に有益なフィードバックをたくさん得られる。
  • エラーが減少し、深刻な状況に陥らない。

<デメリット>
多くのインタフェースとパッケージを持つことによる概念的なオーバヘッドが発生する。

  • 妥当な代償。
  • 調査に時間はかかるだろうがトータルで見たら仕事は簡単になる。

ここでいう「概念的なオーバヘッド」は「今までと構成がどう変わったのかを調査したり理解する時間」だと思う。書籍には特に記載されていないが。


まとめ

依存関係がゴチャゴチャしているから理解もしにくいし、結果的に変更に時間がかかる。
依存関係がゴチャゴチャしているから遅延時間が多くなりフィードバックを得るのに時間がかかる、結果的に変更に時間がかかる。

なので具象クラスに依存するのではなくインタフェース(抽象)に依存させることで、それを解決しようぜ、という内容。


第6章のスプラウトやラップでは「とりあえず既存の処理のテストはせず放置。新規に追加修正を入れたところだけテストを書いてメンテする」という内容だった。
「修正箇所だけでなくそれを取り巻く大きなまとまりをメンテする(それができるようになる)」ためのヒントが7章で書かれていたのかな、という所感。


また、最初こそ時間はかかるかもしれないが、一度この仕組みを適用できれば、作業時間は劇的に変わることが書かれていた。
その「最初の一歩」をやるための時間をどうやって捻出するかが課題なのかなと思った。
(結局はプロジェクト次第?「その時間を結局捻出できませんでした。これまでもこれからも今のままいきます」になってしまうと、第7章で書かれていることも夢物語になってしまうと思った)


最後に、この章で述べられていることをひとことで言え、と言われたら「依存関係逆転の法則です」になる。
依存関係逆転の法則についてはググればたくさんでてくるので詳細はそちらで。
自分も「〇〇の法則」系については忘れかけている(忘れている)ところがあるので復習しておかないと・・・。