避免Ruby中的方法重载

时间:2014-11-25 19:44:00

标签: ruby overloading

由于Ruby不支持重载(由于几个微不足道的原因),我试图找到一种“模拟”它的方法。

在静态类型语言中,您不能使用instanceof(当然除了某些特定情况......)来指导应用程序。

所以,记住这一点,这是重载一个我关心变量类型的方法的正确方法吗? (在这种情况下,我不关心参数的数量)

class User
  attr_reader :name, :car
end

class Car
  attr_reader :id, :model
end

class UserComposite
  attr_accessor :users

  # f could be a name, or a car id
  def filter(f)
    if (f.class == Car)
      filter_by_car(f)
    else
      filter_by_name(f)
    end
  end

  private

  def filter_by_name(name)
    # filtering by name...
  end

  def filter_by_car(car)
    # filtering by car id...
  end
end

3 个答案:

答案 0 :(得分:5)

这是一个很好的方法,Ruby为您提供了处理它的工具。

但是你的情况不清楚,因为你的例子与自己相矛盾。如果f.class == Carfilter_by_car接受_car,而不是_car_id

我假设您实际上正在传递该类的实例,如果是这样,您可以这样做:

# f could be a name, or a car
def filter(f)
  case f
  when Car
    filter_by_car(f)
  else
    filter_by_name(f)
  end
end

case [x]查看每个when [y]子句并执行第一个[y] === [x]

实际上这正在运行Car === f。在类对象上调用#===时,如果参数是类的实例,则返回true。

这是一个非常强大的构造,因为不同的类可以定义不同的“大小写相等”。例如,如果参数与表达式匹配,则Regexp类将case等式定义为true,因此以下方法有效:

case "foo"
when Fixnum
  # Doesn't run, the string isn't an instance of Fixnum
when /bar/
  # Doesn't run, Regexp doesn't match
when /o+/
  # Does run
end

答案 1 :(得分:4)

就个人而言,我没有看到以这种方式分支的大问题。虽然使用case

看起来更干净
def filter(f)
  case f
  when Car
    filter_by_car(f)
  else
    filter_by_name(f)
  end
end

稍微复杂的例子涉及用对象替换分支(ruby是oop语言,毕竟:))。在这里,我们为特定格式(类)的数据定义处理程序,然后通过传入的数据类查找这些处理程序。这些方面的东西:

class UserComposite
  def filter(f)
    handler(f).filter
  end

  private
  def handler(f)
    klass_name = "#{f.class}Handler"
    klass = const_get(klass_name) if const_defined?(klass_name)
    klass ||= DefaultHandler
    klass.new(f)
  end

  class CarHandler
    def filter
      # ...
    end
  end

  class DefaultHandler # filter by name or whatever
    def filter
      # ...
    end
  end
end

答案 2 :(得分:0)

您的架构中可能存在潜在问题 - UserComposite需要对CarUser了解太多。假设您需要添加更多类型? UserComposite会逐渐变得臃肿。

但是,很难提供具体的建议,因为过滤背后的业务逻辑不明确(架构应始终适应您的实际用例)。

您需要对CarUser进行一些共同的操作吗?

如果不是,请勿将该行为合并为一个UserComposite类。

如果是这样,您应该使用具有通用接口的装饰器。大概是这样的:

class Filterable
  # common public methods for filtering, to be called by UserComposite
  def filter
    filter_impl  # to be implemented by subclasses
  end
end

class FilterableCar < Filterable
  def initialize(car)
    @car = car
  end
  private
  def filter_impl
    # do specific stuff with @car
  end
end

class DefaultFilterable < Filterable
  # Careful, how are you expecting this generic_obj to behave?
  # It might be better replace the default subclass with a FilterableUser.
  def initialize(generic_obj)
    # ...
  end
  private
  def filter_impl
    # generic behavior
  end
end

然后UserComposite只需关心它是否传递了Filterable,而它所要做的就是在该对象上调用filter。拥有通用的可过滤接口可以保持代码的可预测性,并且更容易重构。

我建议您避免动态生成可过滤的子类名称,因为如果您决定重命名子类,那么找到生成代码的代码就会困难得多。