TDD by Example书中的例子Ruby版
早上看了blackanger写的TDD by Ex这本书里的资金例子,自己也想写一写。和他不同,我是全过程详细写出来。第一次用Ruby写代码,第一次用Ruby的Unit框架,而且下午睡醒后迷迷糊糊写的,可能有很多错误,请多多指正。
第一次迭代后的代码(简单的TDD代码)
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'dollar' class TestMoney < Test::Unit::TestCase def testMultiplication five = Dollar.new(5) five.times(2) assert_equal(10, five.amount) end end
# file doller.rb class Dollar def initialize(amount) @amount = amount end def times(multiplier) @amount = @amount * multiplier end def amount return @amount end end
第二次迭代后的代码
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'dollar' class TestMoney < Test::Unit::TestCase def testMultiplication five = Dollar.new(5) product = five.times(2) assert_equal(10, product.amount) product = five.times(3) assert_equal(15, product.amount) end end
# file doller.rb class Dollar attr_reader :amount protected :amount def initialize(amount) @amount = amount end
def times(multiplier) return Dollar.new @amount * multiplier end end
第三,四次迭代后的代码(添加了相等性测试,刚好Ruby中的equal?和==的语意和Java相反)
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'dollar'
class TestMoney < Test::Unit::TestCase def testMultiplication five = Dollar.new(5) product = five.times(2) assert_equal(10, product.amount) product = five.times(3) assert_equal(15, product.amount) end def testEquality assert(Dollar.new(5) == Dollar.new(5)) assert(Dollar.new(5) != Dollar.new(6)) end end
# file doller.rb class Dollar attr_reader :amount protected :amount def initialize(amount) @amount = amount end def times(multiplier) return Dollar.new @amount * multiplier end def ==(obj) return obj.amount == @amount end end
第五,六,七次迭代后的代码(短命的Franc对象登场)
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'money' require 'dollar' require 'franc'
class TestMoney < Test::Unit::TestCase def testMultiplication five = Dollar.new(5) assert_equal(Dollar.new(10), five.times(2)) assert_equal(Dollar.new(15), five.times(3)) end
def testEquality assert(Dollar.new(5) == Dollar.new(5)) assert(Dollar.new(5) != Dollar.new(6)) assert(Franc.new(5) == Franc.new(5)) assert(Franc.new(5) != Franc.new(6)) assert(Franc.new(5) != Dollar.new(5)) end
def testFrancMultiplication five = Franc.new(5) assert_equal(Franc.new(10), five.times(2)) assert_equal(Franc.new(15), five.times(3)) end end
# file doller.rb class Dollar < Money def initialize(amount) super(amount) end def times(multiplier) return Dollar.new(@amount * multiplier) end end
# file franc.rb class Franc < Money def initialize(amount) super(amount) end def times(multiplier) return Franc.new(@amount * multiplier) end end
# file money.rb class Money attr_reader :amount protected :amount def initialize(amount) @amount = amount end def ==(obj) return obj.amount.equal?(@amount) end end
第八,九,十,十一次迭代(消除子类,很巧妙的一步)
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'money'
class TestMoney < Test::Unit::TestCase def testMultiplication five = Money.dollar(5) assert_equal(Money.dollar(10), five.times(2)) assert_equal(Money.dollar(15), five.times(3)) end
def testFrancMultiplication five = Money.franc(5) assert_equal(Money.franc(10), five.times(2)) assert_equal(Money.franc(15), five.times(3)) end
def testEquality assert(Money.dollar(5) == Money.dollar(5)) assert(Money.dollar(5) != Money.dollar(6)) assert(Money.franc(5) != Money.dollar(5)) end
def testCurrency assert_equal("USD", Money.dollar(1).currency) assert_equal("CHF", Money.franc(1).currency) end end
# file money.rb class Money attr_reader :amount, :currency protected :amount
def initialize(amount, currency) @amount = amount @currency = currency end
def self.dollar(amount) return Money.new(amount, "USD") end
def self.franc(amount) return Money.new(amount, "CHF") end
def times(multiplier) return Money.new(@amount*multiplier, @currency) end
def plus(addend) return Money.new(@amount + addend.amount, currency) end
def ==(obj) return obj.amount.equal?(@amount) && (obj.currency == @currency) end end
最后的一部分 最后是完成不同货币之间的计算,引入了两个新的对象负责处理汇率的Bank和货币相加的Sum对象。
由于Ruby的动态性无须让Sum和Money实现同一接口,反正原书中的Expression对象也是为了Sum和Money对象可以通信。或许可以把Sum简化掉,有空时再想想。
# file tc_doller.rb $:.unshift File.join(File.dirname(FILE), "..", "src") require 'test/unit' require 'money' require 'bank' require 'sum'
class TestMoney < Test::Unit::TestCase def testDollarMultiplication five = Money.dollar(5) assert_equal(Money.dollar(10), five.times(2)) assert_equal(Money.dollar(15), five.times(3)) end
def testFrancMultiplication five = Money.franc(5) assert_equal(Money.franc(10), five.times(2)) assert_equal(Money.franc(15), five.times(3)) end
def testEquality assert(Object.new != Money.dollar(1)) assert(Money.dollar(5) == Money.dollar(5)) assert(Money.dollar(5) != Money.dollar(6)) assert(Money.franc(5) != Money.dollar(5)) end
def testCurrency assert_equal("USD", Money.dollar(1).currency) assert_equal("CHF", Money.franc(1).currency) end
def testSimpleAddition five = Money.dollar(5) sum = five.plus(Money.dollar(5)) bank = Bank.new() reduced = bank.reduce(sum, "USD") assert_equal(Money.dollar(10), reduced) end
def testReduceSum sum = Sum.new(Money.dollar(3), Money.dollar(4)) bank = Bank.new() result = bank.reduce(sum, "USD") assert_equal(Money.dollar(7), result) end
def testReduceMoney bank = Bank.new result = bank.reduce(Money.dollar(1), "USD") assert_equal(Money.dollar(1), result) end
def testReduceMoneyDiffentCurrency bank = Bank.new bank.addRate("CHF", "USD", 2) result = bank.reduce(Money.franc(2), "USD") assert_equal(Money.dollar(1), result) end
def testIndentityRate bank = Bank.new bank.addRate("USD", "CHF", 0.5) assert_equal(1, bank.rate("USD", "USD")) assert_equal(0.5, bank.rate("USD", "CHF")) assert_equal(2, bank.rate("CHF", "USD")) end
def testMixedAddition fiveBucks = Money.dollar(5) tenFrancs = Money.franc(10) bank = Bank.new bank.addRate("CHF", "USD", 2) result = bank.reduce(fiveBucks.plus(tenFrancs), "USD") assert_equal(Money.dollar(10), result) end
def testSumTimes fiveBucks = Money.dollar(5) tenFrancs = Money.franc(10) bank = Bank.new bank.addRate("CHF", "USD", 2) sum = Sum.new(fiveBucks, tenFrancs).times(2) result = bank.reduce(sum, "USD") assert_equal(Money.dollar(20), result) end end
# file money.rb class Money attr_reader :amount, :currency
def initialize(amount, currency) @amount = amount @currency = currency end
def self.dollar(amount) return Money.new(amount, "USD") end
def self.franc(amount) return Money.new(amount, "CHF") end
def times(multiplier) return Money.new(@amount*multiplier, @currency) end
def plus(addend) return Sum.new(self, addend) end
def ==(obj) return obj.amount.equal?(@amount) && (obj.currency == @currency) end
def reduce(bank, to) rate = bank.rate(@currency, to) return Money.new(@amount / rate, to) end end
# file bank.rb class Bank attr_accessor :rates @@rates = {}
def reduce(source, to) return source.reduce(self, to) end
def addRate(from, to, rate) @@rates["#{from}-#{to}"] = rate @@rates["#{to}-#{from}"] = 1 / rate end
def rate(from, to) return 1 if(from == to) return @@rates["#{from}-#{to}"] end end
# file sum.rb class Sum attr_reader :augend, :addend
def initialize(augend, addend) @augend = augend @addend = addend end
def reduce(bank, to) amount = augend.reduce(bank, to).amount + addend.reduce(bank, to).amount return Money.new(amount, to) end
def times(multiplier) return Sum.new(augend.times(multiplier), addend.times(multiplier)) end end
- Previous: Ubuntu一星期用后体验
- Next: zh周蟒