---
# System prepended metadata

title: レガシーコード改善ガイド　第3部②
tags: [読書]

---

# レガシーコード改善ガイド　第3部②
## 第25章続き
### 25.13 リンクによる置き換え
擬装したい関数と同じシグネチャを含むダミーのライブラリを作り、includeやCLASSPATH等を調整して本番用と切り離す。
#### こんな場合に役立つ
* オブジェクト指向言語でない場合。
* 外部ライブラリを擬装する場合。
#### 手順
1. 擬装したい関数（またはクラス）を特定する。
2. それらの代替関数（または擬装クラス）を作成する。
3. 本番用ではなく、代替関数（または擬装クラス）を使うようにビルドを調整する。

### 25.14 コンストラクタのパラメータ化
コンストラクタの外部でオブジェクトを生成し、コンストラクタにはパラメータとしてオブジェクトを渡す。
外部のオブジェクトとの小さな依存関係が生まれるが、一般的には影響は小さい。
#### こんな場合に役立つ
* コンストラクタとオブジェクトとの依存関係を排除したい場合。
#### 手順
1. パラメータ化したいコンストラクタを特定して、そのコピーを作る。
2. そのコンストラクタのパラメータに、生成を置き換えたいオブジェクトを追加する。元のオブジェクト生成処理は削除して、パラメータからインスタンス変数にオブジェクトを代入する。
3. 利用している言語でコンストラクタ→コンストラクタ呼び出しが
* 可能：古いコンストラクタの中身を削除して、新しいコンストラクタの呼び出しで置き換える。→new演算子を用いて、オブジェクトを生成して新しいコンストラクタを呼び出す。
* 不可能：コンストラクタ間の重複するメソッドを抽出する。

### 25.15 メソッドのパラメータ化
メソッドの外部でオブジェクトを生成し、メソッドにはパラメータとしてオブジェクトを渡す。
シグネチャが異なれば、同じ名前の複数メソッドを1クラスに持たせることができる。
#### こんな場合に役立つ
* メソッドとオブジェクトとの依存関係を排除したい場合。
#### 手順
1. 置き換えたいメソッドを特定して、そのコピーを作る。
2. 置き換えたいメソッドのパラメータに、生成処理を置き換えるためのオブジェクトを追加する。その後元の生成処理は削除し、パラメータからオブジェクトを保持する変数への代入処理を追加する。
3. コピー元のメソッドの中身を削除して、パラメータ化したメソッドを呼び出すようにする。元のオブジェクトはそのメソッド内で生成する。

### 25.16 パラメータのプリミティブ化
テストで必要なパラメータを、中間表現を挟むことによって依存関係をなくすことができる。ただ、コードの貧弱さが浮き彫りとなるため、スプラウトクラスを使うともっと手軽にできる。
#### こんな場合に役立つ
* 後でクラスをテストで保護する時間が取れると確信できる場合。
* スプラウトクラスの抽出に向けた第一歩としたい場合。
#### 手順
1. そのクラスに必要と思える処理を独立した関数として作り上げる。その過程でその関数に必要な中間表現を盛り込む。
2. 新しく作った関数に対して必要な中間表現を渡すため、対象のクラスにメソッドを追加する。
3. 置き換えたいメソッドのパラメータに、生成処理を置き換えるためのオブジェクトを追加する。その後元の生成処理は削除し、パラメータからオブジェクトを保持する変数への代入処理を追加する。
4. コピー元のメソッドの中身を削除して、パラメータ化したメソッドを呼び出すようにする。元のオブジェクトはそのメソッド内で生成する。

### 25.17 メソッドと変数の引き上げ
あるクラスにある、ひとまとまりのメソッド群に対して作業する場合、それらのメソッド群はインスタンス生成を阻害する依存関係とは無関係である場合がある。
こうした場合に、メソッドと変数の定義を抽象スーパークラスに持たせ、具象クラスと分離する。
#### こんな場合に役立つ
* メソッドとオブジェクトとの依存関係を排除したい場合。
* 静的メソッドの公開や、メソッドオブジェクトの取り出しなどが困難な場合・
#### 手順
1. 引き上げたいメソッドを特定する。
2. 特定したメソッドを持つクラスに対して、抽象スーパークラスを作成する。
3. 作成した抽象スーパークラスに特定したメソッドを移動して、コンパイルする。
4. コンパイラのエラーに従い、必要な参照を抽象スーパークラスに移動する。また、シグネチャの維持は忘れずに行う。
5. 両方のクラスのコンパイルが成功したら、抽象スーパークラスの配下にサブクラスを作成し、テストで必要なメソッドを追加する。

### 25.18 依存関係の押し出し
問題のある依存関係が広範囲に及ぶ場合に、問題のある依存関係をそのクラスの他の部分から分離することで、テストハーネスで簡単に扱えるようにする。
#### こんな場合に役立つ
* 依存関係が広範囲である場合。
* ライブラリなどの特定の深い処理を切り離したい場合
#### 手順
1. テストハーネス内で、依存関係に問題のあるクラスのビルドを試みる。
2. 問題のある依存関係をビルドで識別する。
3. 新しいサブクラスを作成して、依存関係を取り巻く状況を表す名前をつける。
4. インスタンス変数とメソッドを新しいサブクラスにコピーする。また、シグネチャの維持は忘れずに行う。コピーしたインスタンス変数は元のクラスから削除して、protectedやabstractに変更して、元のクラスを抽象クラスとする。
5. 抽象スーパークラスの配下にテスト用サブクラスを作成し、それをインスタンス化するようにテストを変更する。
6. テストをビルドして、その新しいクラスをインスタンス化できることを確認する。

### 25.19 関数ポインタによる関数の置き換え
置き換えたい関数と同一名称の関数を作り、関数ポインタによって利用する関数を切り替えて依存関係を排除する。
コンパイル時点で完全に排除が可能である。
手続き型言語において、比較的小さな依存関係を排除したいときに用いる。
#### こんな場合に役立つ
* オブジェクト指向言語でない場合。
* 規模の小さな依存関係を排除したい場合。
#### 手順
1. 置き換えたい関数の宣言を見つける。
2. 各関数宣言の前に、同じ名前で関数ポインタを定義する。
3. 宣言した関数ポインタと名前が重複しないように、元の関数宣言の名前を変更する。
4. ソースファイル内で、元の関数のアドレスを使ってポインタを初期化する。
5. ビルドを行い、元の関数の本体部を見つけて新しい関数に置き換える。

### 25.20 getメソッドによるグローバル参照の置き換え
getメソッドを定義してグローバル要素を取得して、グローバル要素の依存関係を回避する。

#### こんな場合に役立つ
* グローバル要素を取得することで依存関係を排除したい場合。
#### 手順
1. 置き換えたいグローバル参照を特定する。
2. グローバル参照に対するgetメソッドを記述する。そのメソッドの可視性が、サブクラスでオーバーライド可能（protected以上）になっていることを確認する。
3. グローバル要素の参照を、getメソッドの呼び出しで置き換える。
4. テスト用サブクラスを作成し、getメソッドをオーバーライドする。

### 25.21 サブクラス化とメソッドのオーバーライド
実装クラスをスーパークラスとする。そのサブクラスをテストクラスとして作成し、メソッドをオーバーライドする。
最も基本的な依存関係の解消法の一つ。そのほかの多くの手法はこの手法の変形である。
#### こんな場合に役立つ
* 抽象クラスと具象クラスに分け、擬装クラスを作成して依存関係を排除したい場合。
* オブジェクト指向型で簡単な依存関係を排除したい場合。
#### 手順
1. 分離したい依存関係や検出を行いたい場所を特定する。目的を達成するためにオーバーライドできる最も小さなメソッドのグループを見つける。
2. 各メソッドをオーバーライド可能にする。（Java→finalを削除する。C++→仮想関数に変換する）
3. 使用している言語で必要ならば、サブクラスでオーバーライドできるようにメソッドの可視性を調整する。(protected以上）
4. サブクラスを作成してメソッドをオーバーライドする。

### 25.22 インスタンス変数の入れ替え
インスタンス変数を入れ替えるメソッドを作成し、変数の入れ替えを行う。多くの場合はファクトリメソッドの抽出とオーバーライドで実現できるが、仮想関数の呼び出しができないC++等において、コンストラクタのパラメータ化が困難な場合に用いる。
#### こんな場合に役立つ
* 仮想関数の呼び出しが使えない場合。
* コンストラクタのパラメータ化が難しい場合。
#### 手順
1. 入れ替えたいインスタンス変数を特定する。
2. supersedeXXX（変数名）と命名したメソッドを作成する。
3. 入れ替えたい変数のインスタンスを破棄し、新しい値をセットするために必要なコードを書く。
4. 参照型の変数の場合、他から呼び出されている場合は置き換えによる影響を確認する必要があるかもしれない。

### 25.23 テンプレートによる再定義
テンプレート型のクラスで再定義することで依存関係を解消する。
型の別名を定義できる機能を利用して、コード全体の参照を変更することなく依存関係を排除できる。
但し、システム上の依存関係が増加してしまうデメリットもある。
#### こんな場合に役立つ
* 型の別名機能が備わっている場合。
* テンプレート型の定義が行える場合。
#### 手順
1. テストする必要のあるクラスの中から、置き換えたいデータを特定する。
2. クラスをテンプレートに変換する。置き換える必要のある変数名を使って、そのクラスをパラメータ化し、ヘッダーの中にそのメソッドの本体をコピーする。
3. テンプレートに別の名前をつける。
4. テンプレートを定義した後でtypedef文を定義し、元のデータ型を指定したテンプレートの別名として、元のクラス名を定義する。
5. テストファイルでは、テンプレートの定義をインクルードし、テスト時に置き換えたい新しい型を使ってテンプレートをインスタンス化する。

### 25.24 テキストによる再定義
依存関係を排除するため、コード解釈時にその場でメソッドを再定義する。
Ruby等の新しいインタープリタ言語に搭載されている。
但し、プログラム終了まで置き換わったままになっているため注意が必要である。
なお、C言語ではプリプロセッサ接合部が同様の役割を果たす。
#### こんな場合に役立つ
* テスト時のみ関数を置き換えたい場合。
* Ruby等の新しい言語を使用する場合
#### 手順
1. 置き換えたい定義を含むクラスを特定する。
2. テスト用ソースファイルの先頭に、クラスを含むモジュールの名前でrequire句を追加する。
3. 置き換えたい各メソッドに対して、テスト用ソースファイルの先頭に代替の定義を記述する。

###### tags: `読書`