# レガシーコード改善ガイド 第2部⑤ ## 第21章 同じコードを至る所で変更しています レガシーコードにつきものなのが重複である。 同じ変更を何度もせねばならず、全体的に汚いシステムになってしまう。 リファクタリングで重複を取り除けるが、その手間をかけるかどうかが重要である。 ### 21.1 最初のステップ リファクタリング開始 * 重複はまず小さなところから取り除いていくのが良い。後で大きな重複を見つけやすくなる。 * 同様の振る舞いを持つ複数のクラスがある場合は、共通する振る舞いをスーパークラスに持つと良い。そこからさらに重複を見つけてスーパークラスに入れていく。 * 2つのメソッドがほぼ同じであれば、異なる部分だけを別メソッドに抽出する。その結果、2つのメソッドは等しくなり、片方を取り除ける。 * 名前の省略形はなるべく避ける。スペルミスの温床になりかねない。 ### 21.2 リファクタリングの効果はあったのか * 新しいコマンドを追加するときは、**コマンドのサブクラスを作成**すればよいだけである。 * 文字列以外で構成されたコマンドを送る場合でも、**数値を文字列に変換したのと同様の機構を使う**事で解決できる。 * 違う形式のコマンドが必要になった際も、コマンドのサブクラスで**メソッドのオーバーライド**を行えば簡単に対応できる。 * いずれも、**振る舞いが1メソッド内に限定されている**ことで、**簡単に変更や追加が可能となる。** * 重複を取り除かねば作業時間は間違いなく多くなる。一方で、重複を取り除くことで、**非常に小さく焦点の絞られた直交性のメソッドが生まれる。** * また、重複を取り除くことによって、**自然と設計が姿を現す**ことにもつながる。 ## 第22章 モンスターメソッドを変更する必要がありますが、テストを書くことができません ここでのモンスターメソッドとは、何百・何千行にも及ぶ、あまりにも長くて複雑なメソッドのことを指す。 このような途方もないメソッドに遭遇した場合、どうすればよいのだろうか。 ### 22.1 モンスターの変種 #### 22.1.1 箇条書きメソッド **箇条書きメソッド**は、**ほとんどインデントされていない**メソッドである。 ただ単にコードの塊が列挙されているだけで、メソッド全体としてのインデントがなっていない。基本的にセクション間の空白は当てにならず、一時変数を別セクションで使っている場合もある。 それでも自分の位置を保てる分、まだ良心的である。 #### 22.1.2 錯乱メソッド **錯乱メソッド**は、インデントされた**1つの大きなセクション**から構成されるメソッドである。 1つの大きな条件文を持っているものが単純な例であるが、まだ箇条書きメソッドと同等品質なだけましである。 ひどいものになると、複数のif文が何重にもネストされたものになり、もはやどの中カッコがどの振る舞いをしているのか分からず、テストも書きにくくなる。 このような場合、**リファクタリングツールでメソッドの抽出をサポートしているか**どうかで明暗が分かれる。サポートしていない場合はさらに整理が困難になる。 ### 22.2 自動リファクタリングツールでモンスターに立ち向かう リファクタリングツールを使う際は、**そのツールで何ができて何ができないのかを把握しておく必要がある。** 全てのリファクタリングをやってくれるわけではなく、大規模なメソッド分解に必要な、補助的なリファクタリングを行ってくれない場合もある。 大規模なメソッドに対してリファクタリングツールを効果的に適用するには、**ツールのみを用いて一連の変更を行い、その他のソースの変更を一切行わない**ことが挙げられる。こうすることで、安全だと分かっている変更とそれ以外の変更とを明確に区別できる。 自動リファクタリングツールを用いることで、**コード内容が高レベルで伝わり、依存関係を排除するための接合部もできる。こうした変更を安全に行え、細かい処理(名前の変更等)はテストが整備できた後に回せる**というメリットがある。 ### 22.3 手作業によるリファクタリングに挑戦 手作業によるリファクタリングによる正確性の担保は、本来はテストコードで行う。 しかし、モンスターメソッドにおいては、リファクタリングや機能追加が非常に困難である。 幸いなことにこうした場合でもいくつかの手法は使える。 #### 22.3.1 検出用変数の導入 **クラスに変数を追加し、リファクタリング対象メソッドの状態を検出する手法である。** 必要なリファクタリングが終わったら、変数を取り除いてきれいな状態に戻す。 **検出用変数を用いる**ことで、内部の複雑な処理の結果を知ることができる。 ### 22.3.2 理解している部分の抽出 **小さいもの(3~5行程度の、簡単に名前が付けられるコードの塊)** から手を付けて、テストが無くても自信をもって抽出できる小さなコードを探し、0000そこからそのコードに対するテストを作成する方法である。 注意すべき点は、抽出の「**結合カウント**」である。抽出対象のメソッドに受け渡しされる値の数のことで、この数が小さい抽出ほど失敗しにくい。(但し、インスタンス変数に対するアクセスは対象外である。) メソッドの抽出時に一番危険なのは、型変換エラーが起きることである。こうしたエラーも小さい抽出ほど起きにくい。 結合カウントが0より大きい場合は、しばしば**検出用変数の導入**が役に立つ場合がある。 なお、リファクタリングツールが無い場合は、**結合カウントが0のメソッドだけを抽出する**ことで、テストやその先の作業に向けての良い準備となる。 #### 22.3.3 依存関係の落穂拾い **依存関係の落穂拾い**は、変えてはならない部分のロジックに対してテストを書き、それ以外の範囲のコードを抽出することで、**重要な振る舞いが変わらないことを担保する手法**である。 アプリケーションの全ての振る舞いが重要なわけではないため、**非常に重要な部分だけをまずテストで保護する**ことで、多くの変更が可能になる。 非常に重要な振る舞いが、他の振る舞いと絡み合っている時に有効である。 #### 22.3.4 メソッドオブジェクトの取り出し 検出用変数は強力なツールであるが、検出に最適な変数がメソッドのローカル変数になっている場合がある。 インスタンス化するにしても、抽出したメソッドを単独で呼び出した場合に変数がどうなっているかを理解するのは困難である。 こうした場合に用いるのが**メソッドオブジェクトの取り出し**である。モンスターメソッドからメソッドオブジェクトを取り出すと、そのクラス唯一の責務がモンスターメソッドの仕事になる。**メソッドは新しいクラスのコンストラクタのパラメータとなり、モンスターメソッドのコードは新しいクラスのメソッド内に含まれることになる。** コードを新しいクラスに移動すると、リファクタリングは非常に容易になる。 ### 22.4 戦略 #### 22.4.1 骨組みメソッド 条件文を含むコードの中からメソッドを抽出する場合、条件部分と処理部分を分けて2つのメソッドに抽出する方法である。 このメソッドは「骨組みメソッド」といい、メソッドに残るのは**骨組み=制御構造**と、**他のメソッドへの委譲のみ**になる。 #### 22.4.2 処理シーケンスの発見 骨組みメソッドとは異なり、条件部分と処理部分を一緒に抽出する方法である。 この方法を用いることで、**共通の処理シーケンスを特定しやすくなる。** **骨組みメソッドと処理シーケンスの発見は互いに行き来できる。状況に応じてどちらから先に行うかを決定する。** 大抵の場合は箇条書きメソッドに対しては処理シーケンスの発見、錯乱メソッドの場合は骨組みメソッドを検討する。 #### 22.4.3 まず現在のクラス内で抽出する **抽出を検討しているコードが、実は別のクラスのものであると気づく場合がある。** 他のクラスで使っている変数と同じ名前を抽出したメソッドに付けようとした場合、それはその他クラスに属するべきメソッドと言える。 但し、**直接該当するクラスに抽出したいと思っても、最初は我慢する。不適切な名前であってもそのまま名前を付ける。** 別のクラスへのメソッド移動は、変更をどの方向に進めれば良いのかが自然に明らかになった段階で、いつでも実行できる。 それまでは現在のクラス内にとどまるほうが、エラーの可能性を減らせる。 #### 22.4.4 小さい部分の抽出 まずは小さいメソッドから抽出すること。 小さな変化でも、その先に進むべき道が見えてくることが多い。 最初から大きく分けるのは簡単でも安全でも無いため避ける。 #### 22.4.5 抽出をやり直す覚悟 モンスターメソッドの分解にも様々な方法がある。 いくつか抽出を行ってみると、大抵の場合より良い方法が見つかる。 先に進むにはいくつかの抽出をやり直した方が良い場合もある。その場合でも最初に行った抽出という経験は無駄にはならない。 ## 第23章 どうすれば何も壊していないことを確認できるでしょうか? コードはほっておいても、何回動かしても壊れないが、編集するとたちまち壊れてしまう。 バグは偶然物がキーボードに当たって文字が加わった結果起こるものも多い。 コードは非常に壊れやすい材料である。そのようなリスクを緩和する方法を紹介する。 ### 23.1 超集中編集 コード編集時に、ソフトウェアの振る舞いを変えるキー入力と、変えないキー入力がある。 振る舞いを変えるキー入力には、コード部分の文字や数字の変更等がある。 振る舞いを変えないキー入力には、インデントやコメント、デッドコードの変更等がある。 いずれにせよ、**少しでもキー入力が振る舞いに与える影響が分かれば、バグを減らすことに役立つ。** この点で、**テスト駆動開発**は非常に有効である。 テストによって**超集中編集(物事に没頭し、細心の注意を払ってコードを扱える状態)** を行うことが可能である。 ### 23.2 単一目的の編集 一度に多くの作業をまとめて行おうとすると、非常に大変な状況になる。 **一度に一つのことを行うことは非常に大切である。** チームで行う場合、お互いに一度に一つのことをやっていることを確認し合うのが賢明である。 ### 23.3 シグネチャの維持 リファクタリングは、ミスの危険性を伴う。 テストがあればよいが、レガシーコードでは、先にリファクタリングをしてからテストを書くという手順になることが多い。 この場合、リファクタリングはより保守的に行わねばならない。そのような場合に、**シグネチャの維持**は有効である。 **メソッドのシグネチャ全体をカット・コピーしてからペーストすると、エラーを作りこむ可能性を最小限に抑えることができる。** #### 手順 1. 引数リスト全体をバッファにコピーする。 2. 新しいメソッド宣言を入力する。 3. バッファの内容を新しいメソッド宣言にコピーする。 4. 新しいメソッドの呼び出しを入力する。 5. バッファの内容を呼び出し部分にコピーする。その後型を削除し引数名だけにする。 ### 23.4 コンパイラまかせ 静的に型付けされた言語の場合、**コンパイラの型チェック機能を使う事で、必要な変更部分を洗い出す**ことができる。 これを**コンパイラまかせ**と呼ぶ。 但し、継承等のエラーを起こしやすい場面も存在するため、やみくもに使用するのではなく、**その限界を理解しておく**必要がある。 #### 手順 1. 宣言を変更してコンパイルエラーを起こす。 2. エラーが発生した部分について変更を行う。 ### 23.5 ペアプログラミング **ペアプログラミング**は、品質を高めたり、チーム内で知識を広めたりする際に非常に有効である。 ## 第24章 もうウンザリです。何も変更できません レガシーコードでの作業は困難である。それ故にプログラマとしての純粋な楽しさが奪われ、落胆しやすくなる。 レガシーコードで成功するカギはやりがいを見出すことである。大きなコミュニティと交流したり、少しだけテスト駆動開発を行ってみたりと、プログラマとしての楽しさがレガシーコードでも生かせるようになると考えると、やりがいを見出すことができる。 ###### tags: `読書`