2011-07-22 37 views
40

¿Cómo puedo forzar una subclase para implementar un método en Ruby? No parece haber una palabra clave abstracta en Ruby, que es el enfoque que tomaría en Java. ¿Hay alguna forma más similar a Ruby para hacer cumplir el resumen?Método abstracto en Ruby

+0

Además, Ruby utiliza algo llamado Typing Pato: http://en.wikipedia.org/wiki/Duck_typing – mikeycgto

+7

@delnan, no hubo necesidad de expresar su respuesta de esa manera. Si estuviera tratando de mantenerme en una mentalidad Java, no estaría pidiendo una solución tipo "Ruby". Gracias, sin embargo, por su sugerencia sobre la excepción de tiempo de ejecución. –

+2

Lo siento si salí grosero. Acabo de ver * tantas * personas tratando de programar en el lenguaje A como si fuera el lenguaje B.Tu pregunta también se parecía un poco a esto, cuando preguntaste cómo hacer lo que las clases abstractas hacen en Java (en lugar de "un rubí equivalente a clases abstractas" o algo así como likr zhsz). Nuevamente, sin ofenderse, quizás te equivoqué. – delnan

Respuesta

43

Se supone que los métodos abstractos son menos útiles en Ruby porque no es fuertemente estáticamente tipado.

Sin embargo, esto es lo que hago:

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. 

Rara vez parece ser necesaria, sin embargo.

+7

Ruby está fuertemente tipado. Simplemente no está tipado estáticamente. –

+0

Siempre me sale mal. Ta, editado. – Andy

+1

+1, así es como se hace en Smalltalk con 'subclassResponsibility' ('^self subclassResponsibility'). –

7

Mi enfoque preferido es similar pero ligeramente diferente ... lo prefiero como sigue, porque hace que el código de auto-documentado, que le da algo muy similar a 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 

Algunas personas se quejan de que esto es menos SECO e insiste en crear una subclase de excepción y/o poner la cadena "SubclassResponsibility" en una constante, pero en mi humilde opinión you can dry things up to the point of being chafed, and that is not usually a good thing. P.ej. si tiene múltiples clases abstractas en su base de código, ¿dónde definiría la constante de cadena MESS?!?

+0

¿Qué tal un beat em con un símbolo :)? Entonces es una constante que flota alrededor, no una cadena en cada caso. Me gusta tu respuesta – baash05

+0

única objeción, es que las clases secundarias tendrían que implementar no_abstract_method también, de lo contrario cuando se llamara (presumiblemente para probar), se llamaría al método uno y se podría llamar al método 2. – baash05

4

me gusta el uso de una joya como abstract_method que da una conexión DSL de sintaxis estilo rieles métodos abstractos:

class AbstractClass 
    abstract_method :foo 
end 

class AbstractModule 
    abstract_method :bar 
end 

class ConcreteClass < AbstractClass 
    def foo 
    42 
    end 
end 
14

me gusta la respuesta por pvandenberk, pero me gustaría mejorarlo de la siguiente manera:

module Canine  # in Ruby, abstract classes are known as modules 
    def bark 
    fail NotImplementedError, "A canine class must be able to #bark!" 
    end 
end 

Ahora si usted hace una clase que pertenece a Canine "clase abstracta" (es decir, una clase que tiene el módulo Canine en sus antepasados), se quejará si se encuentra que el método #bark no está implementado:

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!" 

Tenga en cuenta que ya que las clases de Ruby no son estáticas, sino que siempre abierta a los cambios, Dog clase en sí no puede hacer cumplir existencia de #bark métodos, ya que no sabe cuándo se supone que debe ser terminado. Si usted como programador lo hace, le corresponde a usted probarlo en ese momento.

1

Este código no le permitirá cargar la clase si los métodos 'foo', 'bar' y 'mate' no están definidos en la clase heredada.

No tiene en cuenta las clases que se definen en muchos archivos, pero seamos honestos, ¿muchos de nosotros realmente definen los métodos de clase en muchos archivos? Quiero decir si no cuentas mezclas. (Que esto no cuenta para)

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 
0

Si usted quiere tener produce un error cuando se crea una instancia de la clase que usted podría hacer lo siguiente

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 
Cuestiones relacionadas