http://martinfowler.com/bliki/IsFixingAnUnknownBugRefactoring.html

リファクタリングの境界線のひとつ

Przemyslaw Pokrywka が興味深い難題を投稿してくれた。 リファクタリング本Null Object の導入が載っている ——これは非常に有用なリファクタリングである (Joshの新刊でも触れられている)。 Przemyslaw は、このリファクタリングは振る舞いを変化させてしまうと指摘した。 例えば、null を戻すメソッドがあるとする。 戻ってきた null に対してメソッドを呼び出そうとすると、 Null Pointer Exception が発生する。 だが Null Object を使うと、デフォルトの振る舞いが得られる。

多くのリファクタリングは振る舞いを変化させる。 リファクタリングとは、振る舞いを変化させることを目的としているのだ。 Form Template Method を使うと、 プログラムの振る舞いは変わってしまう。 ここで重要となるのは、ここで言う「振る舞い」が リファクタリングの定義のなかで述べた 「外部から見たときの振る舞い」なのかどうかだ。 これは、本来プログラム行うべき振る舞いを変更しているだろうか? Null Object を導入するときは、 戻り値を操作している個所に注意してプログラムを見渡さなければならない。 通常、戻り値が null かどうかのチェックを行うことになる。 このことが Null Object の導入をトリッキーなリファクタリングにしてしまっている。

この難題の興味深い点は、バグの個所を見過ごしたときに起こる。 プログラムのどこかで、null の参照に対してメソッドを呼び出すとする。 リファクタリング前だと、exception が発生してしまうだろう。 我々は、あなたがこのことを知らず、 わざわざトップレベルのハンドラーまで原因をさぐりに行くんだなと考える。 リファクタリングを行えば、デフォルトの振る舞いを得ることができる ——これはバグのフィックスかもしれない。 Null Object の導入によって未知のバグをフィックスしたが、 これはリファクタリングと呼べるだろうか?

私はリファクタリングと呼べると思う。 バグを含んだ振る舞いを知らなかった(あるいは気にしなかった)わけだから、 これは「外部から見たときの振る舞い」ではないのだ。 たとえバグに気付いていたとしても、 それが気にするようなバグでなければ、 リファクタリングと呼んでもよいと思う。

この例は興味深い。 自分の考えを変えてしまったり、 極端な例を調査したりしてしまいそうである(★)。

ここで指摘される興味深い点のひとつに、 手動リファクタリングとツール駆動リファクタリングの違いがある。 手動リファクタリングではこういった判断が必要だが、 ツールを使う場合は、もっと注意する必要がある。 ツールだと、振る舞いの保持を常に保証することはできない ——ファイルから名前を読み取り、リフレクションを使ってメソッドが呼ばれた場合、 メソッドのリネームでさえもリファクタリングを破壊してしまう(★)。