レガシーコードに向き合うために レガシーコード改善ガイド を読んでいます。
スプラウトメソッド、スプラウトクラス という用語を知らなかったので理解のために整理しました。
レガシーシステム開発時によくある状況
テストが存在しない既存のメソッドに、新たな要件を追加する場合を考えます。
安易に考えると既存のメソッドに直接新たなコードを追加してしまいますが、以下のような問題が発生する可能性があります。
- 既存のコードをより混乱させてしまう。
- 新しく追加したコードと既存のコードの区別がない。
- テストが書けない。
私は日々の業務でこのような状況によく遭遇します。
このような場合には、スプラウトメソッドやスプラウトクラスの利用を検討します。
スプラウトメソッド(Sprout Method)
既存のメソッドに直接新たな処理を追加するのではなく、新しくメソッドを用意して処理を追加します。
既存のメソッドからは新しいメソッドを呼び出すようにします。
こうすることで、少なくとも新しいコードに対してはテストを書くことができます。
スプラウト(sprout)は「発芽させる」、「新芽」を意味します。
著者はレガシーコード内にテスト済みのコードが追加される様子を「発芽」に例えています。
長所
- 既存のコードと新しく追加したコードを明確に区別できる。
- 既存のコードをより混乱させてしまうことがない。
- 新しいコードはテストで保護できる。
短所
- 既存のコードを改善するわけでも、テストで保護するわけでもない。
スプラウトクラス(Sprout Class)
依存関係が絡まりあった状況では、スプラウトメソッドを適用してもテストが書けない場合があります。
その場合は、スプラウトクラスの適用を検討します。
スプラウトクラスとは、変更に必要な機能を別のクラスとして切り出し、そのクラスを元のクラスから利用する手法です。
本書ではC++でサンプルが書かれていますが、Javaで簡単に記載してみます。
前提として、QuarterlyReportGenerator という巨大なクラスがあったとします。
このクラスはインスタンス化が難しく、スプラウトメソッドを作ってもテストを書くことが難しい状況とします。
複数行にわたって文字列を結合している generate()
メソッドに、新たな文字列(ヘッダー)を追加する場合を考えます。
/**
* 四半期報告書を作成するクラス
*/
public class QuarterlyReportGenerator {
public String generate() {
String pageText;
/**
* 複数行にわたってpageTextに文字列を結合して返す処理。
*/
return pageText;
}
}
上記のメソッドに直接処理を追加するのではなく、ヘッダーを作成するクラスを生成し、それを利用するようにします。
/**
* 四半期報告書のヘッダーを作成するクラス
*/
public class QuarterlyReportTableHeaderGenerator {
public String generate() {
String header;
/**
* ヘッダーを生成する処理。
*/
return header;
}
}
本書では、
インタフェースに相当するクラスを作成し、両方のクラスに継承させることで、コードの共通性を表現できます。
としているため、それにならいインタフェースを用意してみます。
public interface HTMLGenerator {
public String generate();
}
各クラスは次のようになります。
/**
* 四半期報告書を作成するクラス
*/
public class QuarterlyReportGenerator implements HTMLGenerator {
@Override
public String generate() {
String pageText;
/**
* 複数行にわたってpageTextに文字列を結合して返す処理。
* ここでQuarterlyReportTableHeaderGeneratorを利用する。
*/
return pageText;
}
}
/**
* 四半期報告書のヘッダーを作成するクラス
*/
public class QuarterlyReportTableHeaderGenerator implements HTMLGenerator {
@Override
public String generate() {
String header;
/**
* ヘッダーを生成する処理。
*/
return header;
}
}
長所
- 複雑な依存関係から抜け出せる。
- コードを直接書き換える方法よりも、確信をもって変更を進められる。
短所
- 仕組みが複雑になる。
- 本来一つのクラスにあるべき処理が分散してしまう。
最後に
- 本書では、スプラウトメソッドやスプラウトクラスを追加する際の手順、テストを書く際の具体的なプラクティスなども説明されています。詳しくは本書を参照ください。
- 本書では、これらの説明の後、ラップメソッド、ラップクラスの説明が続きます。スプラウトメソッド、スプラウトクラスが唯一の改善策ではない点に注意ください。