这看起来像Ruby的鸭子打字一样吗?

时间:2011-05-26 04:26:17

标签: ruby duck-typing

我创建了一个跟踪汽车里程和服务历史记录的程序,以便更新用户以了解汽车即将到来的服务需求。

我有三个课程:CarCarHistoryCarServiceHistoryEntry。第三个是直截了当的;它包含与服务相关的所有属性:日期,里程,执行的服务等。CarHistory类如下:

require_relative 'car_service_history_entry'

class CarHistory
  attr_reader :entries
  def initialize (*entry)
    if entry.size > 1
      @entries = []
    else
      @entries = entry
    end
  end
  def add_service_entry entry
    @entries << entry
  end
  def to_s
    entries_string = ""
    @entries.each {|entry| entries_string << "#{entry.to_s}\n"}
    entries_string
  end
end
  1. initialize中,是否应该检查entry的班级?
  2. add_service_entry中,采用鸭子打字(如Andy Thomas在“编程Ruby”中的论点),我是否会测试是否可以添加CarServiceHistoryEntry?我不能只是传递String而不是设置,然后在我的单元测试中添加CarServiceHistoryEntry吗?
  3. 由于CarHistory的唯一必要属性是entries数组和to_s方法,我应该将这个类全部废弃并放入car类?

3 个答案:

答案 0 :(得分:1)

很难对CarHistory课程与其他人的关系发表评论,但我相信随着你的进展你会很清楚。

您的一些方法可以简化,但我必须说我不理解if中的initialize,也许它只是倒退而应该是> 0。< / p>

def initialize *entry
  @entries = entry # if not specified it will be [] anyway
end

def to_s
  @entries.join "\n"
end

是的,Ruby应该很简单。您不需要使用运行时类型检查来丢弃代码。如果代码运行您的单元测试,那么您可以宣布胜利。数以万计的显式转换往往会修补类型错误。

Ruby无论如何都会在运行时检查你的类型。将类型检查留给口译员并将您的精力投入到功能测试中是完全合理的。

答案 1 :(得分:1)

对于1和2,当你转向像Ruby这样的松散类型的语言时,你需要释放对“严格打字”的紧密控制。

  • 你应该查看你的输入参数吗?传统的答案是肯定的。另一种方法是使用良好的名称和单元测试来记录和指定类型应该如何工作。如果它与其他类型一起使用,那就好..这是一个额外的好处。因此,如果传入一个不兼容的类型,它将会出现异常,这在大多数情况下都足够好。尝试一下,看看它的感受(可能的结果:解放/“退却!”。但试试吧。)。 例外情况是,如果您正在为共享库设计公共API - 其中规则不同。您需要快速失败并提供信息,以便输入错误。
  • 至于将car_history俱乐部变成汽车 - 我会问你的汽车课程的责任是什么。如果保持自己的历史就是其中之一,那么你可以将它们归为一类。将来,如果您发现许多与汽车历史相关的方法,您可以再次撤销此决定并再次提取CarHistory类型。使用SingleResponsibilityPrinciple做出明智的决定。这只是OOP - Ruby不会降低对象设计。

代码片段:代码可以更简洁

# just for simplicity, I'm making HistoryEntry a string, it could be a custom type too
class CarServiceHistoryEntry << String
end

class CarHistory
  attr_reader :entries
  def initialize(*history_entries)
    @entries = history_entries
  end

  def add_service_entry(entry)
    @entries << entry
  end
  def to_s
    @entries.join("\n")
  end
end

irb>x = CarHistory.new("May 01 Overhaul", "May 30 minor repairs")
irb>x.add_service_entry("June 12 Cracked windshield")
irb>x.to_s
=> "May 01 Overhaul\nMay 30 minor repairs\nJune 12 Cracked windshield"

答案 2 :(得分:0)

我将跳过前两个问题并回答第三个问题。如果CarServiceHistoryEntry的唯一属性是字符串,则是,废弃CarHistory(以及CarServiceHistoryEntry)并向Car添加service_history属性,这只是一个字符串数组。除非另有证明,否则更简单。

对于鸭子打字,你永远不会想要测试某个'是'是'只看'它是否'响应'(最多)。

最后,回答问题#1,不应该更简单:)

希望这有帮助, 布赖恩