レガシーコード改善ガイド3

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

遅延時間

外部ライブラリなどの依存性がひどいシステムのコードは、ビルドする度に未変更箇所すべてもビルドしなおす必要がある。
その間ぼーっと待つことになる。(遅延時間) 改善済コードだとビルド~テスト実行の時間が短いので、即座にフィードバックが得られる。

ビルドの依存関係の排除

あるクラスをテストコード上でインスタンス生成できても、まだ依存関係は存在する。
それはそのクラスオブジェクトを使用するすべてのクラスである。
この依存関係を排除するためには、クラスのインターフェイスを分離する必要がある。
→ publicな操作をInterfaceに移動し、クラスはそれを実装する。
このときにインターフェイスと実装クラスは別プロジェクトに分けること!それをしないとこの変更の意味は半減してしまう!

依存関係逆転の法則(Dependency Inversion Principle : DIP)

インターフェイスに依存する場合、その依存は通常、とても小さく目立たないものです。
インターフェイスを変更しなければ、コードを変更する必要はありません。そして、インターフェイスを変更することは、背後にある実装コードの変更に比べると、はるかに稀です。
インターフェイスがあれば、そのインターフェイスお使用するコードに影響を及ぼすことなく、インターフェイスの実装クラスを編集したり、新しい実装クラスを追加したりすることができます。
このような理由により、具象クラスに依存するよりも、インターフェイスや抽象クラスに依存するほうが優れています。
 変更の少ないものに依存することで、ある変更が巨大なコンパイルを引き起こしてしまう可能性を小さくすることができます。

どうやって機能を追加すればよいのでしょうか?

テスト駆動開発

1,失敗するテストケースを記述する。*1
2,コンパイルする。
3,ビルドエラーになるので、テスト対象のメソッドを追加する。戻り値(がある場合)は適当なものを返すようにする。
4,テスト実行する。失敗することを確認。
5,テスト対象のメソッド実装する。
6,テスト実行する。成功することを確認。
7,重複を確認。必要であればリファクタリングを行う。テスト実行を行い、成功することを確認する。*2
を繰り返す。

差分プログラミング

機能追加を行う度にサブクラスを作成すると、追加した複数の機能に対応させたい要件が発生した場合に困ったことになる。その場合の対応。
最も安直な改善方法の一例として、コンストラクタにパラメーターを渡し、有効機能の切り替えを行うというものが考えられるが、パラメーターが大量に増えた場合に複雑なコードと化するであろう。
これに対応するには、切り替えが必要な操作を別クラスに分離するという方法が対応策の1つとして考えられる。

リスコフの置換原則(LSP)

あるクラスのサブクラスはベースクラスと同様に扱うことができること。利用者側からしたら親子の関係なく使用する。
この原則を違反しないように気をつけることとしては、
1,親クラス(具象クラス)の具象メソッドをオーバーライドしない。
2,オーバーライドする必要がある場合は、親クラスメソッドが呼び出せるようにする。
原則に従っているクラス構成を「正規化された階層」と呼ぶ。

*1:1つのメソッドに対するテスト項目が複数ある場合、それぞれ別のテストケースとして定義すること。

*2:手順5では成功するように、とりあえず実装している。これをこのステップで整備する。