由于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
答案 0 :(得分:5)
这是一个很好的方法,Ruby为您提供了处理它的工具。
但是你的情况不清楚,因为你的例子与自己相矛盾。如果f.class == Car
则filter_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
需要对Car
和User
了解太多。假设您需要添加更多类型? UserComposite
会逐渐变得臃肿。
但是,很难提供具体的建议,因为过滤背后的业务逻辑不明确(架构应始终适应您的实际用例)。
您需要对Car
和User
进行一些共同的操作吗?
如果不是,请勿将该行为合并为一个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
。拥有通用的可过滤接口可以保持代码的可预测性,并且更容易重构。
我建议您避免动态生成可过滤的子类名称,因为如果您决定重命名子类,那么找到生成代码的代码就会困难得多。