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()中にファクトリメソッドを使用するという、小さな修正を行うリファクタリングをした
  • 類似のリファクタリングを大きなステップで行った

やっと折り返し?かな^^;
毎回思うけど、少ないページにほんと情報量が多いよね、この本。