这看起来像Ruby的鸭子打字一样吗?
问题描述:
我创建了一个跟踪汽车里程和服务历史的程序,以便更新用户即将到来的汽车服务需求。这看起来像Ruby的鸭子打字一样吗?
我有三类:Car
,CarHistory
和CarServiceHistoryEntry
。第三个是直截了当的;它保持了与服务相关联的所有属性:日期,里程数,服务执行等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
- 在
initialize
,应该之类的entry
进行检查? - 在
add_service_entry
,采用鸭子打字(如Andy Thomas在“Programming Ruby”中的说法),我甚至会测试是否可以添加CarServiceHistoryEntry
?难道我只是通过String
而不是在我的单元测试中设置并添加CarServiceHistoryEntry
? - 由于
CarHistory
的唯一必要属性是entries
数组和to_s
方法,我应该只将这个类全部取消并放入car
类中?
答
对于1和2,当您移动到松散类型的语言(如Ruby)时,您需要释放对“严格打字”的严格控制。
- 你应该检查你的输入参数吗?传统的答案是肯定的。另一种方法是使用良好的名称和单元测试来记录和指定类型应该如何工作。如果它与其他类型一起工作,罚款..这是一个额外的好处。所以如果你传入一个不兼容的类型,它会被一个异常炸毁,这在大多数情况下是足够好的。尝试一下,看看它的感受(可能的结果:解放/“撤退!”,但给它一个公平的尝试)。 例外情况是,如果您正在为共享库设计公共API - 其中规则不同。你需要快速和信息错误地输入。
- 至于把car_history装进汽车 - 我会问你的汽车类的责任是什么。如果保持自己的历史就是其中之一,那么你可以将它们分开。将来,如果您发现许多方法与汽车历史有关,您可以再次反转此决定并再次提取CarHistory类型。使用单一责任原则做出明智的决定。这只是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"
答
很难评论CarHistory
类与其他人的关系,但我相信随着你的发展,它会变得清晰。
两三你的方法可以简化,但我必须说,我不明白在initialize
的if
,也许它只是倒退,应该已经> 0
。
def initialize *entry
@entries = entry # if not specified it will be [] anyway
end
def to_s
@entries.join "\n"
end
是的,Ruby应该很简单。您不需要使用运行时类型检查来清理代码。如果代码运行你的单元测试,那么你可以声明胜利。无论如何,显式转换的无数次都倾向于纠正类型错误。
Ruby无论如何都会在运行时检查你的类型。将类型检查留给解释者并将你的努力投入功能测试是完全合理的。
答
我会跳过前两个问题,回答第三个问题。如果CarServiceHistoryEntry的唯一属性是一个字符串,那么是,废弃CarHistory(以及CarServiceHistoryEntry),并将一个service_history属性添加到Car中,该属性只是一个字符串数组。直到事实证明,否则,越简单越好。
至于鸭子打字,你永远不会想测试'是'是'只看它是否'回应'(最多)。
最后,回答问题#1,没有它应该是更简单:)
希望这有助于 布赖恩