接合部を利用して、本番コードとテストコードを切り替える
ここまでの作業により、既存のコードであるRegisterクラスの処理はほとんど変えずに、scanメソッドをテストで保護できました。Registerクラスのすべてのメソッドをテストで保護できたわけではありませんが、このRegisterクラスに機能を追加する場合、少なくともscanメソッドの振る舞いが変わらないことを確認できます。
RegisterクラスのgetItemメソッドでは、継承関係を利用して、テスト時に本番コードと異なる実装を利用できるようにしました。このように、もとのコードを編集しなくてもコードの振る舞いを変えることができる場所のことを『レガシーコード改善ガイド』では「接合部」(Seam)と呼んでいます。接合部を利用することで、テストの際に本番コードを書き換えなくても、テストコードを実行するように切り替えることが可能になります。
カプセル化よりも単体テストの方が重要
ところで、この例では、getItemメソッドをprivateからprotectedに変更しました。これはカプセル化の観点からすると、少々問題がありそうです。しかし、そのことと引き替えに、テストが可能になりました。カプセル化を弱めることと、単体テストを整備することはどちらが重要でしょうか?『レガシーコード改善ガイド』の著者であるマイケル・フェザーズ氏は、テストの方が重要だと強く主張しています。確かに既存のコードであれば、テスト用のサブクラスを作るために、既存クラスのメソッドをprivateからprotectedに変えたとしても、既存の振る舞いにすぐに影響が出ることはないはずです。レガシーコードに取り組むときには、一時的に設計が悪くなったとしても、テストを用意することのほうが重要と言えるでしょう。
テストを書くことで、設計の改善方法が分かる
さてRegisterクラスの問題は、度重なる変更でコードが大きくなりすぎていることでした。その結果、たくさんのprivateメソッドと、多すぎる責務を持ってしまっています。設計の観点からは、1つのクラスにたくさんの責務があることは好ましくありません。
先ほどはscanメソッドをテストで保護するために、多くのprivateメソッドのうちの1つであるgetItemメソッドをオーバーライドできるように変更しました。このことは、getItemメソッドの持つ、商品データベースにアクセスする責務を別のクラスに切り出せる可能性を示しています。具体的な方法としては、Registerのサブクラスを作ってそこに追い出す方法や、抽出した別のクラスに委譲する方法などが考えられるでしょう。実際にどのような設計構造に変更すべきかについては、Registerクラスのその他のメソッドや責務を勘案して判断する必要があります。いずれにしても、テストを整備しておけば、自信を持って安全にリファクタリングを行えるようになるはずです。
このように、レガシーコードをテストで保護しようとすることで、責務を分割し、設計を改善する方法にしばしば気づかされます。これは、テストを書くためには依存関係を排除する必要があり、実際にテストを書くことで、依存関係を排除する具体的な方法が分かるからです。レガシーコードのテストを書くことには、たんにテストを整備する以上の効果があります。
テストで保護するための24のリファクタリング手法
今回紹介した手法は、『レガシーコード改善ガイド』の中で「サブクラス化とメソッドのオーバーライド」(Subclass and Override Method)という名前で紹介されているものです。これ以外にも、この本では24のリファクタリング手法を紹介しています。これらの手法のうちいくつかは、Martin Fowlerの著書『リファクタリング』で紹介されている手法と同様のものですが、テストがないレガシーコードに対してテストを用意することを目的としている点に大きな特長があります。興味を持った方はぜひ『レガシーコード改善ガイド』を読んでみてください。