Как заставить подкласс реализовать метод в Ruby. В Ruby не существует абстрактного ключевого слова, которое я бы использовал в Java. Есть ли еще один Ruby-подобный способ применения абстрактных?
Абстрактный метод в Ruby
Ответ 1
Абстрактные методы, как предполагается, менее полезны в Ruby, потому что они не сильно статически напечатаны.
Однако это то, что я делаю:
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.
Однако это редко кажется необходимым.
Ответ 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 OK:
Dog.new.bark #=> "Bow wow!"
Обратите внимание, что поскольку классы Ruby не являются статическими, но всегда открыты для изменений, сам класс Dog
не может обеспечить существование методов #bark
, поскольку он не знает, когда он должен быть завершен. Если вы, как программист, делаете это, вам решать проверить его в такое время.
Ответ 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
Некоторые люди будут жаловаться, что это меньше DRY, и настаивают на создании подкласса исключений и/или помещают строку "SubclassResponsibility"
в константу, но IMHO вы можете сушить вещи до такой степени, что их изнасиловали, и это обычно нехорошо. Например. если у вас есть несколько абстрактных классов по базе кода, где бы вы определили константу строки MESS
?!?
Ответ 4
Мне нравится использование драгоценного камня, такого как abstract_method, который дает абстрактные методы синтаксиса стиля рельсов dsl:
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 AnstractClass
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