9章 生きている時(times)
ふう、またあいてしまった。
通貨を実装するには・・・と考えてはダメ
通貨に対してどんなテストをしたいか?
必要以上のオブジェクトを確実にできないようする
フライウェイト【DP】のファクトリを使う
- 通貨を表現する複雑なオブジェクトを所有
- インスタンスをできるだけ共有させて、無駄にnewしない
まずは文字列で十分
- 通貨を文字列として返す
- インスタンスから取得できるようにする
テストクラス
public void testCurryency() { assertEquals("USD",Money.dollar(1).curryency()); assertEquals("CHF",Money.franc(1).curryency()); }
まずはそれぞれ返すように実装
Moneyクラス
public abstract String curryency();
Dollarクラス
public String curryency(){ return "CHF"; }
Francクラス
public String curryency(){ return "USD"; }
テスト>グリーン
さて、ここからリファクタリング
両方のクラスで同じ実装にしたい
- 通貨をインスタンス変数に保存
- 保存した変数を返す
まずは、Francクラス
private String currency; public Franc(int amount) { this.amount = amount; this.currency = "CHF"; } public String currency(){ return currency; }
次、Dollarクラス
private String currency; public Dollar(int amount) { this.amount = amount; currency = "USD"; } public String currency(){ return currency; }
変数宣言とcuurency()をスーパークラスへ
- リファクタリング>プルアップ
Moneyクラス
protected String currency; public String currency() { return currency; }
Francクラス、Dollarクラスの以下の部分を削除
private String currency; public String currency(){ return currency; }
テスト>グリーン
この定数文字列を静的なファクトリメソッドに移動してやると、2つのコンストラクタが同一になる>共通の実装にできる
Fracクラス
public Franc(int amount, String currency) { this.amount = amount; currency = "CHF"; }
2つのエラー
- Moneyクラスのfranc()メソッド
- Francオブジェクトを作るファクトリメソッド
- Francのtimes()メソッド
Moneyクラス修正
public static Money franc(int amount) { return new Franc(amount,null); }
Francクラス修正
public Money times(int multiplier) { return new Franc(amount * multiplier,null); }
テスト>グリーン
ここで少しそれる
Franc.times()メソッドについて
なぜコンストラクタをよんでいるか?
いま変更すべきか?
- 中断は短い間だけ
- 中断の中断はよくない
なので、timesメソッドを変更
public Money times(int multiplier) { return Money.franc(amount* multiplier); }
戻って、ファクトリメソッドで通貨の文字列が渡せる
Moneyクラス
public static Money franc(int amount) { return new Franc(amount,"CHF"); }
Francクラスのコンストラクタの引数をインスタンス変数にセットするように変更
public Franc(int amount, String currency) { this.amount = amount; this.currency = currency; }
テスト>グリーン
小さくすすめることについて
- この方法でやれではなく、この方法でできるように
- 細かな変更に分割
- 低速のギア
- 小さい手順を繰り返す
この変更をDollarに対しても行う
Moneyクラス
public static Money dollar(int amount) { return new Dollar(amount,"USD"); }
Dollarクラス
public Dollar(int amount, String currency) { this.amount = amount; this.currency = currency; } public Money times(int multiplier) { return Money.dollar(amount * multiplier); }
テスト>グリーン
バランス重要
- 手順が小さすぎたら大きくする
- 不安になれば、小さい手順にする
- ステアリングのプロセス
- 適切な大きさは存在しない
2つのコンストラクタ内の実装は同じなのでスーパークラスへ
Moneyクラス
public Money(int amount, String currency) { this.amount = amount; this.currency = currency; }
Francクラス
public Franc(int amount, String currency) { super(amount, currency); }
Dollarクラス
public Dollar(int amount, String currency) { super(amount, currency); }
これで、times()の実装をスーパークラスに移動して、サブクラスを削除する準備がほぼ整った
まとめ
- フライウェイトのファクトリ>でもはじめは簡単な実装で実現
- ファクトリメソッドを使って、2つの違いを抽出し、コンストラクタを一致させた
- times()中にファクトリメソッドを使用するという、小さな修正を行うリファクタリングをした
- 類似のリファクタリングを大きなステップで行った
やっと折り返し?かな^^;
毎回思うけど、少ないページにほんと情報量が多いよね、この本。