我想知道在Ruby或Rails中是否有一种方法可以强制子类覆盖父方法中的方法(在Java中,您可以使用abstract class/method来实现这一点)。
假设我有以下几个类:
class Pet
end
class Dog < Pet
def collar_color
"red"
end
end
class Cat < Pet
end
dog = Dog.new
dog.collar_color
==> red
cat = Cat.new
cat.collar_color
==> NoMethodError
在这个例子中,我永远不会示例化一个Pet对象,但是它的存在是为了作为一种收集公共类的公共方法的方式。但是让我们假设我想确保Pet的所有子类都覆盖collar_color
方法。有办法做到吗?我可以通过某种方式测试来实现吗?假设我不想在父类中定义一个默认值。
我的实际用例是一个类的集合,所有类都拥有另一个类的多态所有权,如果我有一个被拥有类的显示页面,那么其中一个没有方法的所有者类可能会给我留下一个NoMethodError
问题。
3条答案
按热度按时间gudnpqoy1#
不,没有办法强制执行。
我可以向你保证,不管你能想出什么主意,它都会以某种方式破灭。
首先:静态地做这件事是不可能的。确定一个方法是否被覆盖已经知道等同于解决停止问题。
所以,你必须动态地做,但即使这样也会有问题。
例如:你可以实现
inherited
钩子,并检查是否每个从Pet
继承的类都实现了这个方法。但是,这会阻止某些人继承他们自己的抽象类。(同样,也不能保证inherited
钩子何时运行--它可能在类打开时运行,也就是在定义方法之前。)另外,即使你可以检查这个方法是否存在于类继承
Pet
的地方,这个方法仍然可以在以后被删除,所以你不能得到任何保证。当然,他们可以提供一个伪方法,以绕过你的保护。您 * 可以 * 创建方法的默认实现,这些方法只是
raise
和Exception
,但没有必要这样做:如果您 * 不 * 创建默认实现,那么无论如何raise
已经是一个NoMethodError
异常(如果您 * 这样做 , 不要 * 使用NotImplementedError
,而是使用从RuntimeError
继承自定义异常)。核心库中有这样的示例:例如,
Enumerable
mixin依赖于一个抽象方法each
,而这个抽象方法必须由子类来实现,处理的方法就是简单地记录这个事实:用法
要在集合类中使用模块Enumerable:
#each
。几乎所有可枚举方法都将调用该方法。这实际上是Ruby从一开始就处理任何类型相关问题的方式,因为Ruby没有类型,类型只发生在程序员的头脑中,类型信息只写在文档中。
一直以来都有各种IDE使用的非正式的第三方类型注解语言。最近,引入了两种类型注解语言:RBI,一种由Sorbet类型检查器使用的第三方类型注解语言,以及RBS,一种属于Ruby本身的类型注解语言。
据我所知,RBS没有表示抽象方法的方法,但是RBI does:
如果继承链中的某个子类示例化了一个对象,而这个子类在某个点上没有定义这个方法,那么这会给予你一个类型错误,但是,当然,* 只有 * 代码的用户使用Sorbet这样的类型检查器对代码进行了类型检查,如果代码的用户没有对它进行类型检查,他们不会得到类型错误。
z9gpfhce2#
Ruby的关键字相对较少,但它提供了基本的构建块来实现类似于抽象类或方法的东西。
最简单的形式是在父“abstract”方法中引发一个错误:
__method__
是一个包含当前方法名称的魔术变量。你可以通过创建一个定义“抽象方法”的类方法来使它更优雅一些:
然而Ruby是一种动态语言,你没有编译时检查,所以这只会在调用方法时给予一个稍微明显的错误信息,它实际上并没有给出子类实现方法的任何保证。
这取决于测试或使用像冰沙或RBS这样的类型检查器。一般来说,当你学习忘记你认为你所知道的关于面向对象编程的一切,学习Ruby的方式时,它可能会很有帮助。与Java相比,它有一个非常不同的设计哲学--你使用鸭子类型来查看对象是否响应该方法,而不是抽象的方法和接口。
xqk2d5yq3#
只需通过引发Not implemented error或其他错误在抽象类中定义默认方法实现。通过这样做,您还可以在类设计中澄清,当其他人/您想要继承
Pet
类时,他们需要覆盖collar_color
方法。澄清是一个很好的想法,不在抽象类中定义默认方法没有任何好处。或者如果你想通过测试来实现,你可以为
Pet
类创建一个测试用例,检查它的后代是否定义了自己的collar_color
方法。我认为Rails / Ruby 3.1定义了.descendants
方法,或者你可以谷歌一下。