既存のコードの責務を把握する3つのプラクティス
多すぎる責務を抱えているレガシーコードを改善するための第一歩は、既存のコードの責務を正しく把握することです。そのためのプラクティスとして、第20章では巨大で複雑なコードの責務を把握するための7つの経験則を紹介しています。ここではその中から、3つのプラクティスを紹介しましょう。
プラクティス 1:メソッド分類法
図1に示すRuleParser
クラスを例に考えてみましょう。これは『レガシーコード改善ガイド』に記載されているもので、あるプログラミング言語のルール表現を含む文字列を評価するクラスです。このクラスの責務を見つけるにはどうしたらよいでしょうか。
このクラスには、全部で8つのメソッドがあります。いくつかのメソッドは同じ単語を含む名前を持っています。そこで、名前を基準にしてメソッドを分類してみましょう。evaluate
メソッドとaddVariable
メソッドは、RuleParser
クラスのpublicメソッドになっています。それら以外の6つのprivateメソッドは、名前がExpressionで終わるものと、Termで終わるものがあり、2つのグループに分けられそうです。Expressionで終わるメソッドのグループは、名前が似ているだけでなく引数にNode
クラスを受け取り、整数値を返すという点も同じです。メソッド名から推測できる責務と、メソッド名の分類をまとめると、表1のようになります。
構文解析 | 式の評価 | 字句解析 | 変数の管理 |
evaluate | branchingExpression causalExpression variableExpression valueExpression |
nextTerm hasMoreTerms |
addVariable |
このように名前に着目してメソッドを分類すると、しばしば関連性の強いグループにまとめることができます。このRuleParser
クラスの場合は、4つのグループがそれぞれ異なる責務を持つため、将来的に別々のクラスとして独立させることもできそうです。メソッド分類法(method grouping)は、メソッドが持つ名前に着目してメソッドを分類し、その分類を責務として把握する方法です。
プラクティス 2:機能スケッチ
クラスの責務を見つける別のやり方として、クラスの内部的な関係に着目する方法があります。それが機能スケッチ(feature sketch)を描いて、クラス内の機能的な「まとまり」を把握する手法です。機能スケッチとは、クラスが持つインスタンス変数とメソッドの間の関連を表現する図のことです。
例として、図2のようなReservation
クラスを考えてみます。
機能スケッチを作成する手順は次のとおりです。
- インスタンス変数を表す丸を描いて、その中に変数名を書く
- メソッドを表す丸を描いて、その中にメソッド名を書く
- メソッドを表す丸から、そのメソッドの中で使用している変数もしくはメソッド、あるいは変更している変数に対して矢印を引く
以上の手順で、Reservation
クラスのソースコードを解析しながら機能スケッチを描くと、図3のようになります(ここでは、『レガシーコード改善ガイド』の手法を拡張して、メソッドを表す丸に色を付けるようにしました)。この図を見ると、Reservation
クラスが持つ変数とメソッドには2つのまとまりがあることが読み取れます。すなわち図中の点線で囲んだ部分と、それ以外の部分です。
クラス内の独立性の高いグループは、しばしばひとまとまりの責務に相当します。このような独立性の高いグループは、比較的容易に別のクラスに抽出することができます。このReservation
クラスの場合、点線で囲んだグループに着目することで、最終的に図4のようにリファクタリングすることもできそうです。このように、機能スケッチを描くことで、クラス内部の依存関係を把握しやすくなるため、クラスの責務を把握するだけでなく、具体的なリファクタリング方法を理解するためにも役立ちます。
プラクティス 3:試行リファクタリング
試行リファクタリング(scratch refactoring)は、コードを理解するための手法です。
試行リファクタリングを行うには、まずバージョン管理システムからソースコードをチェックアウトします。そして、テストは書かずに、メソッドの抽出や変数の移動など、あらゆる方法でリファクタリングを行います。変更したコードはチェックインせずに破棄します。以上が、試行リファクタリングの方法です。
この手法の特長は、リファクタリングした結果を本番用のコードとして採用しないことです。実際にはリファクタリングした結果を使わないので、テストで保護されていないコードでもいろいろなリファクタリング手法を試すことができます。いろいろコードを変更してみることで、コードの構造や責務に対する理解を深めることができます。
コードの理解に必要な設計書がなかったり、メソッド名がいい加減だったり、変数とメソッドの関連が複雑過ぎて機能スケッチをすぐに描けないときには、試行リファクタリングを試してみましょう。思いつくままにコードを修正してみると、だんだんと考えが整理でき、クラスの責務を見つけることができます。
余談になりますが、この「試行リファクタリング」という訳語に関しては、我々翻訳チームでもいろいろ議論をしました。原語がscratch refactoringのため、当初は「スクラッチリファクタリング」という訳語にしていました。しかし、一般的によく使われる「スクラッチ開発」では、「スクラッチ」という言葉を“ゼロから作る”という意味で使っているため、「スクラッチリファクタリング」とすると、レガシーコードを破棄してゼロからコードを書くことのように思われてしまうことを懸念しました。そこでメンバー間で再び議論して、「即興リファクタリング」「破棄前提リファクタリング」などの候補の中から、最終的に「試行リファクタリング」を選んだ経緯があります。