最近、RailsConf 2014の「All the Little Things」を視聴しました。この講演中に、Sandi Metzは、ネストされた大きなifステートメントを含む関数をリファクタリングします。
def tick
if @name != 'Aged Brie' && @name != 'Backstage passes to a TAFKAL80ETC concert'
if @quality > 0
if @name != 'Sulfuras, Hand of Ragnaros'
@quality -= 1
end
end
else
...
end
...
end
最初のステップは、関数をいくつかの小さなものに分割することです:
def tick
case name
when 'Aged Brie'
return brie_tick
...
end
end
def brie_tick
@days_remaining -= 1
return if quality >= 50
@quality += 1
@quality += 1 if @days_remaining <= 0
end
私が興味深いと思ったのは、これらの小さな関数の書き方でした。brie_tick
たとえば、は元のtick
関数の関連部分を抽出して作成されたのではなく、test_brie_*
単体テストを参照して最初から作成されました。これらの単体テストのすべてに合格すると、brie_tick
完了したと見なされました。小さな機能がすべて完了すると、元のモノリシックtick
機能は削除されました。
残念ながら、プレゼンターは、このアプローチが4つの*_tick
機能のうち3つが間違っていた(そしてもう1つは空だった)ことに気づいていないように見えました。*_tick
関数の動作が元の関数の動作と異なる場合がありtick
ます。たとえば、@days_remaining <= 0
in brie_tick
は< 0
- であるべきです- およびでbrie_tick
呼び出された場合、正しく動作しません 。days_remaining == 1
quality < 50
ここで何が問題になっていますか?これはテストの失敗ですか?これらの特定のエッジケースのテストがなかったためですか?または、リファクタリングの失敗-コードを最初から書き直すのではなく、段階的に変換する必要があったためですか?