Ruby:选择性类继承?

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)鉴于我想要封装推断任何给定对象策略名称的逻辑 ,并且我觉得到目前为止我尝试的内容表明策略类是错误的位置,那么您会把这种东西放在哪里呢?

谢谢!


更新

感谢JimLim回答第一部分,Linuxios回答第二部分。两者都提供了非常有用的见解。

顺便说一句,经过思考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方法,他们可以设置一个,但是如果他们调用继承来的那个方法,他们会得到一个异常,告诉他们这样做没有意义。

至于如何决定谁得到“答案”,因为他们都回答了我的问题的一半,我的个人习惯在“平局”情况下是接受那个时间拥有更少声望的人的回答。

感谢你们两位的帮助!


在子类中定义一个 lookup 方法仅仅是用于抛出异常,这样做是个好主意吗? - James Lim
undef_method 可能有效。 - James Lim
@JimLim 这对我来说似乎是一个可行的备选方案,但我的想法是其他人会创建子类,而我更希望从他们的角度来看,当他们继承时,这个方法根本不存在。看起来我们在下面的答案中有一些选择... - Andrew
2个回答

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赋予了您改变这些内容以实现清晰度的强大功能,而不是打破语言约定并产生混淆、不直观的类和子类行为。


感谢分享,我非常欣赏你对POLA的看法。现在我正在琢磨这个问题:我认为查找方法是一个实现细节。它是公开的,因为相关模块中的其他对象调用它,但最好不要在模块外部使用该类,除非从中继承。那么,在这种情况下,哪个更少令人惊讶:一个类方法被继承,但应该被子类忽略,一个类方法被继承,但当它被继承时它的行为有所不同,或者一个子类删除了它不需要的方法? - Andrew
@andrew:嗯,最不令人惊讶的可能是“ac tallying probably number 2”,因为库/其他用户只有在阅读所有文档并且文档中有说明时才会理解它应该被忽略。如果即使调用它是无用的,它仍然表现出预期的行为,我认为这是最不“令人惊讶”的。 - Linuxios

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的特殊类上调用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接