レバレッジメモ: レガシーコード改善ガイド

レガシーコード := テストがないコード

テストを作成するためには対象とするクラスから他のクラスへの影響を把握する必要がある。依存関係を排除しておけばニセのクラスを突っ込んで影響を直接観察できる。

テストをするたびに本番コードを編集するわけには行かない、なのでコードを編集せずにテストに不都合な挙動を変えられる場所が必要である。これをseamという。どのseamも、その挙動を変更するenabling pointを持っている。

日本語で「接合部」っていうとくっつけることに意識が向きがちだけど「そこで切り離せる」というほうが重要なのだな。

既存のレガシーコードに機能追加をする方法

1: スプラウトメソッド
テストされてない既存のコードに書き足すのではなく、新しいメソッドを作ってそれを呼び出すようにし、その新しいメソッドにテストを書く

2: ラップメソッド
テストされていない既存のメソッドの前か後ろに処理を付け足す場合、テストされていないメソッドの名前を変えて、元の名前で古いメソッドと新しく追加する機能の入ったメソッドを呼び出すようにし、その新しいメソッドをテストする。

TDD

  • 1: 失敗するテストを書く
  • 2: コンパイルが通るようにする
  • 3: テストを通過させる。この時既存のコードをなるべく変更しない。「これはFooメソッドとほとんど同じで一部だけ違うから抽象化しよう」と考えずにFooメソッドをコピペして作る。抽象化はリファクタリングなのでテストに通ってから。
  • 4: コードをきれいにする
  • 5: 1に戻る

Liscovの置換原則

サブクラスYのオブジェクトはいつでもスーパークラスXのオブジェクトとして使えなければならない。さもないとユーザがあるX型の変数に入っているオブジェクトが実際にはY型であることを意識しなければいけないようになってしまう。

Nullを渡す

Javaのような実行時のnullに対するアクセスを察知できる言語なら、テスト用に作るのが面倒なオブジェクトが必要なときに単にnullを渡せばいい。必要なものは必要になったときに例外で教えてくれる。

影響の調査の仕方

影響スケッチを描く
変数と戻り値が変わる可能性のあるメソッドをまるで囲む。影響を矢印で書く。
よく設計されていればこれはシンプルになるはず。

_

依存性の排除によってカプセル化が壊れるケースがある。たとえばprivateを外すとクラスの中だけ調査していれば依存性を把握できたのが、パッケージ全体を見なければいけなくなったりする。依存性の排除はテストを書きやすくするための手段であり、目的ではない。同様にカプセル化も依存性をおいやすくするための手段であり目的ではない。著者はテストを優先する。

_

サードパーティのライブラリを直接呼んでいる部分はseamにできたはずのところ。サードパーティライブラリに密結合なせいでテストが出来ないとか本末転倒。ラッパーなどを使って疎結合にする。

後半の具体例は斜め読みできないから今度じっくり読もう。