Ruby 中的抽象方法
Posted
技术标签:
【中文标题】Ruby 中的抽象方法【英文标题】:Abstract Method in Ruby 【发布时间】:2011-10-11 04:03:11 【问题描述】:如何强制子类在 Ruby 中实现方法。 Ruby 中似乎没有抽象关键字,这是我在 Java 中采用的方法。还有另一种更类似于 Ruby 的方式来执行抽象吗?
【问题讨论】:
此外,Ruby 使用了一种叫做 Duck Typing 的东西:en.wikipedia.org/wiki/Duck_typing @delnan,没有必要这样说你的答案。如果我试图坚持 Java 思维方式,我就不会要求“类似 Ruby”的解决方案。但是,谢谢您对运行时异常的建议。 如果我失礼了,我很抱歉。我刚刚看到 这么多 人试图用语言 A 编程,就好像它是语言 B。你的问题似乎也有点像这样,因为你问如何做抽象类在 Java 中所做的事情(而不是“相当于抽象类的红宝石”或类似 zhsz 的东西)。再说一次,没有冒犯的意思,也许我弄错了。 【参考方案1】:抽象方法在 Ruby 中的用处应该不大,因为它不是strongly静态类型的。
但是,我就是这样做的:
class AbstractThing
MESS = "SYSTEM ERROR: method missing"
def method_one; raise MESS; end
def method_two; raise MESS; end
end
class ConcreteThing < AbstractThing
def method_one
puts "hi"
end
end
a = ConcreteThing.new
a.method_two # -> raises error.
然而,它似乎很少需要。
【讨论】:
Ruby 是强类型的。只是不是静态类型的。 +1,这就是在 Smalltalk 中使用subclassResponsibility
(^ self subclassResponsibility
) 的方式。
如果您只是删除AbstractThing
的全部内容,您会得到完全相同的行为:尝试调用method_two
时出现异常。事实上,你得到了一个稍微更好的行为,因为你得到一个RuntimeError
,而不是一个通用的非描述性、非语义的NoMethodError
,它可以准确地告诉你你的代码出了什么问题.
@Jorg。这大体上是正确的,当然除了两件事——首先,我可以提出我喜欢的任何错误;我只是保持这个例子简单。关键是你得到一个更具体的错误。其次,定义抽象类让任何阅读代码的人都清楚你的意图(特别是如果你多次子类化它,通常是这种情况)。
这不一样。如果一个方法是抽象的,你应该在加载类时得到一个错误,甚至不需要显式调用丢失的方法。【参考方案2】:
我喜欢 pvandenberk 的回答,但我会改进如下:
module Canine # in Ruby, abstract classes are known as modules
def bark
fail NotImplementedError, "A canine class must be able to #bark!"
end
end
现在如果你创建一个属于Canine
“抽象类”的类(即一个在其祖先中有Canine
模块的类),如果发现#bark
方法没有实现,它会报错:
class Dog
include Canine # make dog belong to Canine "abstract class"
end
Dog.new.bark # complains about #bark not being implemented
class Dog
def bark; "Bow wow!" end
end
# Now it's OK:
Dog.new.bark #=> "Bow wow!"
请注意,由于 Ruby 类不是静态的,而是始终对变化开放,Dog
类本身不能强制存在#bark
方法,因为它不知道应该何时完成。如果您作为程序员这样做,那么您可以在这个时候对其进行测试。
【讨论】:
我在搜索NotImplementedError
时遇到了 this。
NotImplementedError
的目的是表明当前平台上不可用的方法(例如,在只有基于 nix 的系统具有的 Windows 上调用 API)。 ruby-doc.org/core-2.5.0/NotImplementedError.html【参考方案3】:
我的首选方法类似但略有不同...我更喜欢它如下,因为它使代码自我记录,为您提供与 Smalltalk 非常相似的东西:
class AbstractThing
def method_one; raise "SubclassResponsibility" ; end
def method_two; raise "SubclassResponsibility" ; end
def non_abstract_method; method_one || method_two ; end
end
有些人会抱怨这不太干燥,并坚持创建一个异常子类和/或将"SubclassResponsibility"
字符串放在一个常量中,但恕我直言you can dry things up to the point of being chafed, and that is not usually a good thing。例如。如果您的代码库中有多个抽象类,您将在哪里定义 MESS
字符串常量?!?
【讨论】:
用符号击败 em 怎么样 :) 那么它是一个浮动的常量,而不是每种情况下的字符串。喜欢你的回答。 唯一反对的是,子类也必须实现no_abstract_method,否则调用时(大概是为了测试)会调用方法一,可能会调用方法二。【参考方案4】:我喜欢使用像 abstract_method 这样的 gem,它提供了 dsl rails 样式的语法抽象方法:
class AbstractClass
abstract_method :foo
end
class AbstractModule
abstract_method :bar
end
class ConcreteClass < AbstractClass
def foo
42
end
end
【讨论】:
【参考方案5】:如果在继承的类中没有定义方法 'foo'、'bar' 和 'mate',此代码将不允许您加载该类。
它不考虑在许多文件中定义的类,但老实说,我们中的许多人实际上是在许多文件中定义类方法吗?我的意思是,如果你不计算混音的话。 (这确实说明了)
def self.abstract(*methods_array)
@@must_abstract ||= []
@@must_abstract = Array(methods_array)
end
def self.inherited(child)
trace = TracePoint.new(:end) do |tp|
if tp.self == child #modules also trace end we only care about the class end
trace.disable
missing = ( Array(@@must_abstract) - child.instance_methods(false) )
raise NotImplementedError, "#child must implement the following method(s) #missing" if missing.present?
end
end
trace.enable
end
abstract :foo
abstract :bar, :mate
【讨论】:
【参考方案6】:如果你想在创建类的实例时抛出错误,你可以执行以下操作
class AbstractClass
def self.new(args)
instance = allocate # make memory space for a new object
instance.send(:default_initialize, args)
instance.send(:initialize, args)
instance
end
#This is called whenever object created, regardless of whether 'initialize' is overridden
def default_initialize(args)
self.abstract_method #This will raise error upon object creation
end
private :default_initialize
def initialize(args)
# This can be overridden by new class
end
end
class NewClass < AbstractClass
end
NewClass.new #Throw error
【讨论】:
【参考方案7】:因为问题是(专注于)“如何强制子类在 Ruby 中实现方法”,所以我认为我们可以使用 TDD :D,例如:rspec shared example
shared_examples "MUST implement abstract method" do |method_sym|
it is_expected.to respond_to(method_sym)
end
describe Stack do
it_behaves_like "MUST implement abstract method", :push
it_behaves_like "MUST implement abstract method", :pop
end
也许Tests are better than Abstract
:D ,参考:http://morningcoffee.io/interfaces-in-ruby.html
【讨论】:
以上是关于Ruby 中的抽象方法的主要内容,如果未能解决你的问题,请参考以下文章