Comment puis-je forcer une sous-classe à implémenter une méthode dans Ruby. Il ne semble pas y avoir de mot-clé abstrait en Ruby, ce qui serait l'approche que je prendrais en Java. Existe-t-il une autre manière plus ruby d'appliquer le résumé?
Fortement typé statiquement. cependant, voici ce que je fais: extrait de code.
Cela semble rarement nécessaire, cependant.
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.
It rarely seems to be necessary, however.
J'aime la réponse de pvandenberk, mais je l’améliorerais comme suit:
module Canine # in Ruby, abstract classes are known as modules
def bark
fail NotImplementedError, "A canine class must be able to #bark!"
end
end
Maintenant, si vous créez une classe appartenant à Canine
"classe abstraite" (c'est-à-dire une classe qui a le module Canine
dans ses ancêtres), il se plaindra s'il est constaté que la méthode #bark
n'est pas implémentée:
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!"
Notez que, puisque les classes Ruby ne sont pas statiques, mais qu'elles sont toujours ouvertes aux modifications, la classe Dog
ne peut elle-même imposer l'existence de méthodes #bark
, car elle ne sait pas quand est censée être terminée. Si vous le faites en tant que programmeur, c'est à vous de le tester à ce moment.
Mon approche préférée est similaire mais légèrement différente ... Je la préfère comme suit, car elle rend le code auto-documenté, vous donnant quelque chose de très similaire à 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
Certaines personnes se plaindront que cela est moins sec, et insisteront pour créer une sous-classe d’exceptions et/ou pour mettre la chaîne "SubclassResponsibility"
dans une constante, mais IMHO vous pouvez faire sécher les choses jusqu’au point d’être irrité, et c’est généralement bonne chose . Par exemple. Si vous avez plusieurs classes abstraites dans votre base de code, où définiriez-vous la constante de chaîne MESS
?!?
J'aime l’utilisation d’une gemme telle que abstract_method qui donne une méthode abstraite à la syntaxe de style Rails:
class AbstractClass
abstract_method :foo
end
class AbstractModule
abstract_method :bar
end
class ConcreteClass < AbstractClass
def foo
42
end
end
Ce code ne vous laissera pas charger la classe si les méthodes 'foo', 'bar' et 'mate' ne sont pas définies dans la classe héritée.
Cela ne tient pas compte du fait que les classes sont définies dans de nombreux fichiers, mais soyons honnêtes: est-ce que beaucoup d’entre nous définissons réellement les méthodes de classe dans plusieurs fichiers? Je veux dire si vous ne comptez pas les mix-ins. (ce que cela représente)
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
Si vous souhaitez qu'une erreur soit générée lorsque vous créez une instance de la classe, vous pouvez procéder comme suit:
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