同一个方法的不同声明形式( same method in different declarations )
访问量: 2821
之前脑子里一直知道 TDD 会产生设计良好的代码。 但是一直没有落实到纸面上。今天早上遇到了同样的问题,所以现在赶紧记录下来。 ( all over the time, I know that TDD give us "fine designed code". however I haven't write my experience. This morning I had the chance and now write it down )
假设我们有两个对象: TargetUrl , Plan ( assuming we have 2 objects: )
class TargetUrl belongs_to :plan attr_accessible :url end class Plan attr_accessible :naming_urls end
然后我们希望 在TargetUrl中有一个方法,可以根据当前的URL返回 一个file_name: ( and I need TargetUrl has a method: generate a file_name according to its 'url' )
url = "http://site.com/popup_page?foo=111&bar=222" @plan = Plan.create(:naming_rule => ' @file_name = "foo-#{@params[:foo]}-bar-#{@params[:bar]}" ' @target_url = TargetUrl.create(:url => url, :plan_id => @plan.id)
于是,我发现自己在写代码的时候,写出了两个形式的代码: ( so I found that I wrote 2 declarations of this method)
# 直接拿来就写: (先写实现:) ( form 1. implementation first , no TDD here) def generate_file_name_by_url ( 这里没有参数) url = self.url naming_rule = self.plan.naming_url # ... other code end
# 然后它难于测试(在测试之前,需要建立各种model, 想好它们的关系, 先建立plan, 再建立target_url 。而且在方法调用时不知道它使用了哪些变量(都深深的隐藏在instance 中) ), ( then I found it hard to test: I need to setup all the related models and make a method call which is not so easy to see what variables it is using)
# 想了想, 觉得似乎这个方法可以写成一个通用的方法,改成这样: ( so I change it to a ClassMethod) def TargetUrl.generate_file_name_by_url url, naming_rule end # 所以,老老实实的使用TDD 方式, ( in the TDD way: ) # 先写测试: (spec first) before do #... url = "http://site.com/popup_page?foo=111&bar=222" @plan = Plan.create(:naming_rule => ' @file_name = "foo-#{@params[:foo]}-bar-#{@params[:bar]}" ' @target_url = TargetUrl.create(:url => url, :plan_id => @plan.id) #... end it 'should get_file_name_by_url' do file_name = TargetUrl.generate_file_name_by_url @target_url.url, @plan.naming_rule @params = Rack::Utils.parse_query(URI(@target_url.url).query).to_options file_name.should == "foo-#{@params[:foo]}-bar-#{@params[:bar]}" end # 再写实现,发现 class_method 形式是不合理的: ( then I found class method is not suitable here) def TargetUrl.generate_file_name_by_url url, naming_rule @file_name = ... return @file_name end # 于是改成 instance method: ( let's change it to instance method) def generate_file_name_by_url url, naming_rule @file_name = ... return @file_name end
于是现在的 unit test 和 implementation 分别是: ( so for now, the rspec and implementation are)
it 'should get_file_name_by_url' do file_name = @target_url.generate_file_name_by_url @target_url.url, @plan.naming_rule # ... end class TargetUrl # ... def generate_file_name_by_url url, naming_rule @params = Rack::Utils.parse_query(URI(url).query).to_options @file_name = '' eval(naming_rule) return @file_name end end
结论: 不要偷懒, TDD 永远可以让我们设计出最合适的实现方式。 (conclusion: don't be lazy. always TDD, which help us write better-designed code )