6章 再度、すべてに対する等価性

5章でテストを早くパスするために、あえてコピペという罪を犯してかいたコードを綺麗にする


綺麗にするには


スーパークラス

  • それぞれ下位のクラスに対して、等価性がある

各変更ごとにテストを行う


Moneyクラスというスーパークラスを作成

package star;
public class Money {
}

DollarクラスをMoneyクラスの拡張とする

public class Dollar extends Money {
	public int amount; 

ammountインスタンス変数をMoneyに移動(プルアップ)

Dollar

public class Dollar extends Money {

Money

public class Money {
	protected int amount;
}


equalsメソッドを一般化する準備


一時変数の宣言を変更

2つの作業

public boolean equals(Object object){
	Money dollar = (Dollar)object;
	return amount == dollar.amount;
}
public boolean equals(Object object){
	Money dollar = (Money)object;
	return amount == dollar.amount;
}

一時変数自体も変更

public boolean equals(Object object){
	Money money = (Money)object;
	return amount == money.amount;
}

DollarクラスからMoneyクラスへ、equalsメソッドを移動することが可能(プルアップ)

public class Money {
	protected int amount;
	public boolean equals(Object object) {
		Money money = (Money)object;
		return amount == money.amount;
	}
}


Franc.equals()について

  • 削除する必要あり
  • そもそも等価性のテスト(testEqaulity)には、Francは入っていなかった
  • テストを作る必要がある

十分なテストがないと、テストによってサポートされないリファクタリングに遭遇する


リファクタリングに失敗してもテストがパスすることがある


解決にはあればよいと思うテストを作る


悪い循環

  1. リファクタリング中になにかが壊れる
  2. リファクタリングに悪い印象
  3. リファクタリングをしたくなくなる
  4. 設計の質が低下


testEqaulity()にFrancケースを追加

public void testEqaulity(){
	assertTrue(new Dollar(5).equals(new Dollar(5)));
	assertFalse(new Dollar(5).equals(new Dollar(6)));
	assertTrue(new Franc(5).equals(new Franc(5)));
	assertFalse(new Franc(5).equals(new Franc(6)));
}

FrancクラスをMoneyクラスからの拡張に

  • amountフィールドの移動
  • eqaulsメソッドの移動:冗長な実装と分かるよう、実装を一致させる


まとめ

  • クラスからスーパークラスへと段階的に共通コードを移動
  • Francクラスもサブクラス化


次はFrancクラスとDollarクラスの比較