Ruby:选择性类继承?

时间:2013-08-03 02:59:54

标签: ruby inheritance class-method

所以,我一直在玩一些模型,我遇到了一种情况,我真的想通过子类来限制类方法的继承。麻烦的是,我的实验到目前为止证实了我的理解,这是不可能完成的。

我天真地尝试了以下方法:

class Policy
  class << self
    def lookup(object)
      #returns a subclass by analyzing the given object, following a naming convention
    end

    def inherited( sub )
      sub.class_eval { remove_method :lookup }
    end
  end
end

当然这不起作用,因为子类没有方法,它在超类上。之后我尝试了:

def inherited( sub )
  class << Policy
    remove_method :lookup
  end
end

这就像一个魅力,哈哈,除了它的工作的微小细节,通过在第一次加载子类时从超类中取出该方法。糟糕!

所以,由于Ruby查找方法的方式,我很确定这不起作用。

我感兴趣的原因是,在我正在处理的行为中,根据命名约定,您可以有许多不同的策略,并且我希望有一个很好的干净方法来获取对策略的引用任何其他类的对象。对我来说,在语法上,这样做似乎很好:

class RecordPolicy < Policy
  # sets policy concerning records,
  # inherits common policy behavior from Policy
end

class Record
end

$> record = Record.new
=> #<Record:0x0000>
$> Policy.lookup(record)
=> RecordPolicy

但是,我认为能够致电RecordPolicy#lookup没有任何意义。你有这个政策,下面没有任何东西可以找到。

所以,我的问题分为两部分:

1)事实上,是否有某种方法可以有选择地定义Ruby中可以继承的类方法?

此时我非常肯定答案是否定的,因此:

2)鉴于我想封装用于推断任何给定对象某处的策略名称的逻辑,并且它看起来像我到目前为止所尝试的那样表明Policy类是错误的地方,你会把这种东西放在哪里?

谢谢!


更新

感谢JimLim回答第1部分,感谢Linuxios回复第2部分。非常有用的见解。

FWIW,在反思了Linuxios所说的内容后,我决定这样做:

class << self
  def lookup( record )
    if self.superclass == Policy
      raise "No default naming convention exists for subclasses of Policy. Override self.lookup if you want to use it in a subclass."
    else
      # naming convention lookup goes here
    end
  end
end

对于如何使用此代码,我觉得这是最不令人惊讶的事情。如果某人有某种理由在子类上提供#lookup方法,他们可以设置一个,但是如果他们调用一个继承的方法,他们会得到一个异常,告诉他们这样做是没有意义的。

至于如何决定谁得到“答案”,因为他们都回答了我的问题的一半,我个人习惯在“领带”的情况下接受了声誉不佳的人的回答。时间。

谢谢你们的帮助!

2 个答案:

答案 0 :(得分:5)

在JimLim的回答中扩展一些东西。

首先,这是有效的,因为undef_methodremove_method之间存在差异。

remove_method实际上完全删除了该方法。 undef_method,根据文件:

  

阻止当前类响应对指定方法的调用。

(强调我的)。

但是...

如果您的问题是您在问题中显示的问题,我认为您正在考虑这个问题。能够致电RecordPolicy.lookup有什么不对?它可能没用,但它遵循最小惊讶原则。 Policy类是此方法的正确位置,但如果您实现它,请执行以下操作:

def self.lookup(obj)
  if(self.superclass == Policy) #It's a subclass, return self
    return self
  else
    #look stuff up
  end
end

没有什么不合适的。仅仅因为一个方法在一个对象上是无用的,如果它有意义*语言*明智的它在那里,不要乱用它。为了清晰起见,Ruby为您提供了改变这些内容的强大功能,而不是破坏语言约定并使令人困惑,不直观的类和子类化行为。

答案 1 :(得分:2)

undef_method似乎有效。根据文件,

  

阻止当前类响应对named的调用   方法。将其与删除方法的remove_method进行对比   来自特定班级; Ruby仍然会搜索超类和   用于可能的接收器的混合模块。

class Policy
  def self.lookup(object)
  end
end

class RecordPolicy < Policy
  class << self
    undef_method :lookup
  end
end

Policy.lookup nil
# => nil
RecordPolicy.lookup
# => NoMethodError: undefined method `lookup' for RecordPolicy:Class

使用#inherited,我们可以

class Policy
  def self.lookup(object)
  end
  def self.inherited(sub)
    sub.class_eval do
      class << self
        undef_method :lookup
      end
    end
  end
end

class RecordPolicy < Policy
end

RecordPolicy.lookup
# => NoMethodError: undefined method `lookup' for RecordPolicy:Class

使用sub.class_eval { undef_method :lookup }将无效,因为这将取消定义RecordPolicy的实例方法。它需要在RecordPolicy的本征类上调用。