わすれっぽいきみえ

みらいのじぶんにやさしくしてやる

本『レガシーコード改善ガイド』を読み直した

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)

『レガシーコード改善ガイド』をおととしくらいには購入していて、その時一度さっくり読んだけど「まぁ当たり前のことが書かれてるな」という気持ちしかなかった。今レガシーコードを触ってて*1調査も時間がかかるので、自分でやってて「これでいいのか。もしかしてもっといいやり方があるんじゃないか。何か違うやり方をしてるせいで時間がかかり過ぎているんじゃないか」と不安になってきたので改めて読んでみた。

読み直した感想

読み直しても思ったが、やっぱり「当たり前のこと」が書かれてる本だと思う。でも自分のやってることが間違ってないかを確認するのには良かった。間違ってないというか、少なくともこの本に書かれてる内容のうちのいくつかは実践していたもので、今も実際に使ってるから自分の方法自体は大きく外れないな、と思った。

具体的には「第12章 1箇所にたくさんの変更が必要ですが、関係するすべてのクラスの依存関係を排除すべきでしょうか」に出てくる割り込み点と絞り込み点を出してみるというところだ。割り込み点というのは「特定の変更による影響を検出できるプログラム上の場所」で、絞り込み点は「複数ある割り込み点のうち、すべての変更の影響を検出できる場所」のことで、特に絞り込み点は影響スケッチという「メソッドやクラスが呼び出される様子を矢印を引いてわかるようにした絵」の中で、自然と矢印が集中してくるところに当たる。私自身は影響スケッチというものは特に書いてなくて、以下のような箇条書きをよく使っている。

  • ClassA::methodA
    • ClassB::methodB
      • ClassC::methodC
    • ClassD::methodD

この箇条書きは methodAmethodB の中と methodD の中で呼ばれていて、さらに methodBmethodC で呼ばれている、ということを表す。ClassX:: というのはそれぞれのメソッドがどのクラスにあるメソッドなのかを表している。クラス化されてなかったらファイル名を直接書いたりもする。ここから methodA の変更は最終的に methodCmethodD に影響を与えることがわかる。だが methodD の中に条件文があって、特定の条件では methodA が呼ばれないことがわかり、かつ今回加えようとしている変更がまさに呼ばれないパターンに該当する場合には methodD は影響を与える場所としては除外できる、みたいなことを調査しながら箇条書きを編集していく。例えば「 ClassD::methodD 無視」とか自分にわかる書き方でいい。 methodA に変更を加えると必ず methodC に影響が伝わることがわかったなら、絞り込み点は methodC となって、とりあえず methodC のテストを書いたら、仮に methodA のテストが書けなくても動作は一旦保証できるので、 methodA の変更でおかしなことがあったらわかるね、というやり方になる。もしここで ClassB::methodB が他のメソッド内でも呼ばれることがわかったら ClassC::methodC のテストよりも ClassB::methodB のテストを書けるなら書くようにする。そうすることで影響範囲をまさに『絞り込む』。

影響スケッチ自体は書いたことが全くなかったわけではなく何度かはノートに書いたりしていたが問題があって、wikiとかに画像をのせても大体見にくくて「どうやって調べたか」聞かれた時にパッと見でわからずに結局説明し直さなきゃいけなくなることがあることだ。そもそも見にくいということはその時点でもう影響範囲がでかすぎるので、変更を加える場所自体を再考したほうがいいわけだが、「ここを変更したら他を変えなくていいじゃん」といって粘られることとかもあるので、まだ人に見せられる範囲に落とし込めそうで説明に使えそうかなぁと考えていたら自然と箇条書きになった。自分しか見ない影響スケッチなら自分一人のノートに書きなぐってしまえばいいと思ってたし、実際影響スケッチとはそうやって書捨てるようなものではあるが、影響範囲が広がりすぎると自分で描いててよくわからなくなってきて、いらないところを削って書き直して…とやってるうちに、「あれ?結局どの絵を見るのが一番正しいんだっけ?」みたいにもなってくるので、割と箇条書きはシンプルで使い易い気がしている。

絞り込み点を探すのは大事なことだとは思うが、そこの部分のテストがいつでもすんなり書けるように設計されてないことによく遭遇するので、もし初めに見つけた絞り込み点のテストが書けなさそうなら「どこのテストなら書けそうか」で探るのも結構やる。そうすると ClassB::methodB よりも ClassC::methodC かなぁとか考えてくうちに絞られてきたりする。あとテストがあるメソッドは経験的にテストがないメソッドよりも使い易い。

といった感じで12章とかはまさにこういう試行錯誤の話(というか全章に渡ってつらい試行錯誤すること多いよねって話)が書かれてて、悲しいけど「みんなつらいんだな。やっぱ時間かかっちゃうよな。」としぶしぶ納得する。

レガシーコードに触れたことがない人だったら悩まないのかもしれないが、これから触るかもしれない人は一度読んでみた方がある程度時間短縮になるかもしれない。いきなりコードを読み進めてもスパゲッティで迷子とか本当にあるある*2なので、心構えというかどう立ち向かえば突破できそうかヒントを多少知っておくだけでも、いきなり心折られるよりマシだと思う。正直いつも心折れてなんかやってられないから自分のやり方が合ってるか迷った時に精神安定剤的な使い方としてこの本を読むのもアリな気がする。これを読んで逆に「なんでこの章を読んでなかったんだ!早く読んでおいたら良かった!」と逆に発狂する可能性も微レ存。諸刃の剣。私は安心した。よかった。

あと一応、この本は手段であって答えではない。この本に書かれてることをそのまま使えるかどうかはケースバイケースだ。「読んでみたけど答えが書いてなかった」という人にこの本は向いてないと思う。ていうか答えがそんな簡単に転がってるならレガシーとか言わないと思うの。

読む順番について

この本の見出しは「何に悩んでいるか」というトピック形式で書かれているので、自分が抱えている問題に合わせて読みたい章を選ぶといい。でもどれも気になって結局どこから読み始めたらいいのかわからないけど全部を初めから読み通すのは面倒くさいよというそこの私。そう私だ。一応その目安になるページは第2章の『レガシーコードの変更手順』にある。

  1. 変更点を洗い出す
    • 16: 変更できるほど十分に私はコードを理解していません
    • 17: 私のアプリケーションには構造がありません
  2. テストを書く場所を見つける
    • 11: 変更する必要がありますが、どのメソッドをテストすればいいのでしょうか
    • 12: 1箇所にたくさんの変更が必要ですが、関係するすべてのクラスの依存関係を排除すべきでしょうか
  3. 依存関係を排除する
    • 23: どうすれば何も壊していないことを確認できるでしょうか
    • 9: このクラスをテストハーネスに入れることができません
    • 10: このメソッドをテストハーネスに入れることができません
    • 25章は依存関係を排除する手法
    • 22: モンスターメソッドを変更する必要がありますが、テストを書くことができません
    • 7: いつまで経っても変更作業が終わりません(ビルドに時間がかかりすぎるとか)
  4. テストを書く
    • 13: 変更する必要がありますが、どんなテストを書けばよいのかわかりません
  5. 変更とリファクタリングを行う
    • 8: どうやって機能を追加すればよいのでしょうか
    • 20: このクラスは大きすぎて、もうこれ以上大きくしたくありません
    • 21: 同じコードをいたるところで変更しています
    • 22: モンスターメソッドを変更する必要がありますが、テストを書くことができません

上の順番で読んでくと大体網羅できる。14/25章なので読む量を少しは減らせる。感想で述べた第12章は「テストを書く場所を見つける」という項目に含まれる。もういいかなと思ったら、今はもうこの本にお世話になる時じゃないんだと思う。

*1:結構前から触ってるけど、まだ先輩にいろいろアドバイスもらってたのでマシだったんだなと痛感する今日この頃

*2:ねーよって言いたい