RubyMoneyExample

2005-11-18 19:00:39 +0900 (6927d); rev 54

勉強のため『テスト駆動開発入門』の「I. THE MONEY EXAMPLE.」のサンプルコードを Ruby で書いてみます。 少しずつ書き進めていく予定です。

このページの Diff を見ると初期のテストコードから少しずつ書き換えてゆく手順を確認できると思います。

class Money

  attr_reader :amount
  attr_accessor:currency
  protected :amount

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def self.dollar(amount)
    Dollar.new(amount, :USD)
  end

  def self.franc(amount)
    Franc.new(amount, :CHF)
  end

  def ==(other)
    @amount == other.amount && self.class == other.class
  end

end

class Dollar < Money

  def times(multiplier)
    Money.dollar(@amount * multiplier)
  end

end

class Franc < Money

  def times(multiplier)
    Money.franc(@amount * multiplier)
  end

end


if $0 == __FILE__
  require 'test/unit'

  class TestMoney < Test::Unit::TestCase

    def test_multiplication
      five = Money.dollar(5)
      assert_equal(Money.dollar(10), five.times(2))
      assert_equal(Money.dollar(15), five.times(3))
    end

    def test_franc_multiplication
      five = Money.franc(5)
      assert_equal(Money.franc(10), five.times(2))
      assert_equal(Money.franc(15), five.times(3))
    end

    def test_equality
      assert(Money.dollar(5) == Money.dollar(5))
      assert(Money.dollar(5) != Money.dollar(6))
      assert(Money.franc(5) == Money.franc(5))
      assert(Money.franc(5) != Money.franc(6))
      assert(Money.dollar(5) != Money.franc(5))
    end

    def test_currency
      assert_equal(:USD, Money.dollar(1).currency())
      assert_equal(:CHF, Money.franc(1).currency())
    end

  end
end

TO DO リスト

  • レートが2:1の場合、$5+10 CHF = $10
  • ×$5*2=$10
  • ×amountのプライベート化
  • ×Dollarの副作用
  • Moneyの丸め処理
  • ×==
  • hash
  • nil との等価性
  • オブジェクトとの等価性
  • ×5 CHF * 2 = 10 CHF
  • Dollar と Franc の重複
  • ×== の一般化
  • times の一般化
  • ×Franc と Dollar の比較
  • ×通過
  • test_franc_multiplication の削除

第 9 章 Times We're Livin' In.

  1. 通貨の概念を導入
  2. currency メソッドをスーパークラスに移動
  3. :USD と :CHF を静的なファクトリメソッドに移動すればコンストラクタの実装を共有出来る
  4. Dollar に対しても同じ変更をしてみる。テストにパスした
  5. 2つのコンストラクタは全く同じなのでスーパークラスに移動

第 8 章 Makin' Objects.

  1. Dollar を返すファクトリメソッド
  2. Dollar を作成して返す
  3. テスト内の全ての場所でファクトリメソッドを使用する
  4. test_franc_multiplication はまだ残しておく
  5. ※前の章で「Franc と Dollar の比較」を消し忘れていた

第 7 章 Apples and Oranges.

  1. Franc と Dollar を比較すると何が起るのだろうか
  2. amount とクラスが等しいときにだけ Money は等しい
  3. 混合通貨の計算ができないと

第 6 章 Equality for All, Redux.

  1. テストを素早くパスさせるために大量のコピペという大罪を犯した
  2. コードをきれいに。共通のスーパークラス
  3. Money クラス
  4. amount メソッドを移動
  5. おっとテストがなかった
  6. テストができたので Franc に Money を継承させる
  7. ※TO DO リストの更新を忘れていたので追加
  8. Franc と Dollar を比較すると何が起るか

第 5 章 Franc-ly Speaking.

  1. ひとつ目のテストに取り組むには?→依然として急激な飛躍に思える
  2. 必要条件はフラン通貨を表現するオブジェクト
  3. Dollar のテストをコピペ
  4. さらに大量のコードをコピペしてテストを動作させる
  5. 重複がなくなるまで帰宅しないと約束

第 4 章 Privacy.

  1. Dollar 同時を比較するように
  2. インライン化
  3. amount のプライベート化 (Ruby ゆえしかなたく protected)

第 3 章 Equality for All.

  1. バリューオブジェクト
  2. 等価性のテストを追加。バーはレッド
  3. 仮実装
  4. 三角測量を行う
  5. 等価性を一般化

第 2 章 Degenerate Objects.

  1. このテストを動作させるきれいな方法は思い浮かばない
  2. 新しいテストはDollar#timesの宣言を変更しないと

第 1 章 Multi-Currency Money.

  1. TO DO リスト作成
  2. テスト(失敗するテスト)を作成
  3. テストを実行することができた。テストは失敗
  4. テストをパスさせるための最も小さな変更
  5. TO DO をひとつ消す

system revision 1.162