【问题标题】:Does this look right as Ruby's duck typing?这看起来像 Ruby 的鸭子打字吗?
【发布时间】:2011-05-26 04:26:16
【问题描述】:

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

我有三个班级: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 类中?

【问题讨论】:

    标签: ruby duck-typing


    【解决方案1】:

    对于 1 和 2,当您转向像 Ruby 这样的松散类型语言时,您需要释放对“严格类型”的紧握。

    • 您应该检查您的输入参数吗?传统的答案是肯定的。另一种方法是使用良好的名称和单元测试来记录并指定类型应该如何工作。如果它适用于其他类型,很好..这是一个额外的好处。因此,如果您传入一个不兼容的类型,它会因异常而崩溃,这在大多数情况下已经足够了。试一试,看看感觉如何(可能的结果:解放/“撤退!”。但给它一个公平的尝试。)。 例外情况是,如果您正在为共享库设计公共 API - 其中规则不同。对于错误的输入,您需要快速失败并提供信息。
    • 至于将 car_history 加入 car - 我想问一下您的 Car 类的职责是什么。如果保持自己的历史是其中之一,您可以将它们加入俱乐部。以后,如果你发现有很多与汽车历史相关的方法,你可以再次撤销这个决定,再次提取 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】:

      很难评论CarHistory 类与您其他人的关系,但我相信随着您的进展,您会明白这一点。

      你的一些方法可以简化,虽然我必须说我不明白initialize中的if,也许它只是倒退了,应该是&gt; 0

      def initialize *entry
        @entries = entry # if not specified it will be [] anyway
      end
      
      def to_s
        @entries.join "\n"
      end
      

      是的,Ruby 应该很简单。您无需在代码中乱扔运行时类型检查。如果代码运行您的单元测试,那么您可以宣布胜利。无论如何,无数的显式转换往往会修补类型错误。

      Ruby 无论如何都会在运行时检查你的类型。将类型检查留给解释器并投入到功能测试中是完全合理的。

      【讨论】:

        【解决方案3】:

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

        至于鸭式打字,您永远不会想测试某物是否“是”,只看它是否“响应”(最多)。

        最后,回答第一个问题,不应该更简单:)

        希望这会有所帮助, 布赖恩

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-25
          • 2011-05-13
          • 1970-01-01
          • 1970-01-01
          • 2011-03-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多