3

所以,我一直在玩一些模型,并且遇到了一种情况,我真的很想限制子类对类方法的继承。麻烦的是,到目前为止,我的实验已经证实了我的理解,这是无法做到的。

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

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 方法,他们可以设置一个,但如果他们调用继承的方法,他们会得到一个异常,告诉他们这样做没有意义。

至于如何决定谁得到“答案”,因为他们都回答了我问题的 1/2,我个人在“平局”的情况下的习惯是接受当时声誉较低的人的答案。

谢谢你们的帮助!

4

2 回答 2

5

只是要扩展@JimLim 的答案。

首先,这是因为 whatundef_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 为您提供了强大的能力来更改这些内容以保持清晰,而不是打破语言约定并制造令人困惑、不直观的类和子类化行为。

于 2013-08-03T04:43:54.477 回答
2

undef_method似乎工作。根据文件,

防止当前类响应对命名方法的调用。将此与 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 的 eigenclass 上调用。

于 2013-08-03T03:15:52.620 回答