Back

同一个方法的不同声明形式( same method in different declarations )

发布时间: 2014-05-12 02:11:00

之前脑子里一直知道 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 ) 

Back