FrancとDollarのtimesは似ている
Moneyを返すようにすれば一致させることが可能
Francのtimesメソッド
public Money times(int multiplier) { return new Franc(amount * multiplier); }
テスト
Dollarのtimesメソッド
public Money times(int multiplier) { return new Dollar(amount * multiplier); }
テスト
次の手順は明確でない
Franc、Dollarは、存在を正当化するほどの動作を行っていない、削除したい
- timesメソッドのことですよね?
- サブクラス自体のことを言っている?
サブクラスへの直接参照が現象すれば、削除へ近づく
Dollarを返す、Moneyのファクトリメソッドを導入
まずはテストクラスから
public void testMultiplication() { Dollar five = Money.dollar(5); assertEquals(new Dollar(10), five.times(2)); assertEquals(new Dollar(15), five.times(3)); }
Eclipseのエラーを修正
- Moneyクラスのimport
- Moneyクラスへのdollarメソッドの追加
テスト>レッド
Moneyクラスで、Dollarを作成して返す
public static Dollar dollar(int amount) { return new Dollar(amount); }
テスト>グリーン
Dollarへの参照を削除したいので、テストクラスを変更
public void testMultiplication() { Money five = Money.dollar(5); assertEquals(new Dollar(10), five.times(2)); assertEquals(new Dollar(15), five.times(3));
Eclipseのエラーを修正
- Moneyにtimesメソッドを作成
- ひとまずテスト>グリーン
Moneyにtimesメソッドを作る準備ができていない
Money.timesメソッドを抽象化する
public abstract Money times(int i);
もちろん、Moneyクラスを抽象クラスに
public abstract class Money {
テスト>グリーン
ここでMoney.dollarメソッドを変更可能に
public static Money dollar(int amount) { return new Dollar(amount); }
テスト>グリーン
テスト内のすべての箇所でファクトリメソッドを使用可能
public void testMultiplication() { Money five = Money.dollar(5); assertEquals(Money.dollar(10), five.times(2)); assertEquals(Money.dollar(15), five.times(3)); }
public void testEqaulity(){ assertTrue(Money.dollar(5).equals(Money.dollar(5))); assertFalse(Money.dollar(5).equals(Money.dollar(6))); assertTrue(new Franc(5).equals(new Franc(5))); assertFalse(new Franc(5).equals(new Franc(6))); //FrancとDollarの比較 assertFalse(new Franc(5).equals(Money.dollar(5))); }
クライアントはDollarというサブクラスをクライアントコードはしらない
テストをサブクラスから分離したことで、どのモデルコードに影響を与えずに継承を自由に変更可能になった
timesメソッドは抽象化されたし、Money.dollarメソッドはサブクラスを容易に差し替え可能になった
次、testFrancMultiplicationを変更する前に、testMultiplication=Dollarの乗算用テストについて
すべてのロジックがテストされている
テストを削除すると、コードに対して自信を失うだろうか?
- テストクラスからみてサブクラスは関係なくなったということ?
本では、釈然としないが、そのままにしておく
では、Francについても同様に
Moneyクラス
public static Money franc(int amount) { return new Franc(amount); }
テストクラス
public void testMultiplication() { Money five = Money.dollar(5); assertEquals(Money.dollar(10), five.times(2)); assertEquals(Money.dollar(15), five.times(3)); } public void testEqaulity(){ assertTrue(Money.dollar(5).equals(Money.dollar(5))); assertFalse(Money.dollar(5).equals(Money.dollar(6))); assertTrue(Money.franc(5).equals(Money.franc(5))); assertFalse(Money.franc(5).equals(Money.franc(6))); //FrancとDollarの比較 assertFalse(Money.franc(5).equals(Money.dollar(5))); } public void testFrancMultiplication() { Money five = Money.franc(5); assertEquals(Money.franc(10), five.times(2)); assertEquals(Money.franc(15), five.times(3)); }
Money.dollarとおなじ
まとめ
- 同じメソッド、timesをMoneyという同一のシグニチャを利用することで、除々に重複を取り除いた
- dollar、francメソッドのこと、スーパークラスへ宣言の移動
- ファクトリメソッドの導入で、具象クラスの存在をテストから分離
- サブクラスが範囲外になったので、テストが不要になることはわかったが、行動にはうつさなかった
- これがさっきのテストを削除する話に繋がっている
次はtimesメソッドの重複を取り除く
わかりやすくなってきた。
しかもテストを用意している効果が分かりやすくなってきた気がする。