2010-10-23 11 views
9

Soy nuevo en Ruby. Un ejemplo sencillo, lo que necesito:Alternativas a clases abstractas en Ruby?

class Animal 
    abstract eat() 

class Cat < Animal 
    eat(): 
    implementation 

class Dog < Animal 
    eat(): 
    implementation 

En otras palabras, el método de comer() debe ser requerido para todas las clases que se extienden animal.

En JAVA solo usaría una clase abstracta, pero después de hacer algunas investigaciones descubrí que muchas personas no lo usan en Ruby y en su lugar se recomiendan mixin/modules.

Sin embargo, no entiendo, si los módulos pueden hacer algo más que incluir un método de adición. Para ser exactos, ¿pueden los módulos establecer los requisitos de las clases que métodos deben implementar (si es así, se apreciaría un ejemplo)?

En resumen, ¿qué debería usar en este caso, cuando quiero estar seguro, que todas las clases del mismo tipo tienen métodos particulares y los implementan a su manera?

+1

¿Cuál es la cita de 'NotImplementedError' y el tren de aterrizaje de los aviones? –

Respuesta

9

Ruby no ofrece esta funcionalidad, no. Usted es responsable de asegurarse de que sus clases implementen lo que deberían implementar. Parte de la razón por la cual tal funcionalidad es imposible para Ruby es que las clases de Ruby pueden reabrirse y Ruby soporta cargar código arbitrario en tiempo de ejecución, por lo que no podemos saber si una clase implementa una determinada interfaz hasta que intentemos llamar eso.

Suponiendo un Animal debe tener un método eat, y hago lo siguiente:

class Cat < Animal 
    def talk 
    puts "meow" 
    end 
end 

class Cat 
    def eat 
    puts "om nom nom" 
    end 
end 

Al final de ese archivo, el Cat tendrá su definición eat, porque las clases de Ruby se volvió a abrir y modificada varias veces . ¿Debería haber un error de código después de la primera definición porque eat aún no estaba definido? Esa implementación dolería más de lo que ayudaría, ya que las clases de reapertura son comunes, incluso si este ejemplo es artificial. ¿Debería producirse un error una vez que se llama al método eat y no existe, por lo que podemos estar seguros de que se ha definido una vez que lo necesitamos? Bueno, si el método falta, ese sería ocurrirá, de todos modos. El intérprete nunca puede saber si hay otra definición de clase en camino, por lo que nunca puede interrumpirla hasta que se llame realmente al método.

En resumen, las superclases simplemente no pueden requerir que se defina un método en Ruby, porque la naturaleza dinámica de las clases contradice tal objetivo.

¡Lo siento! Sin embargo, este es un lugar donde las pruebas unitarias pueden ser útiles para garantizar que sus subclases hagan lo que se supone que deben hacer.

+0

+1 para "om nom nom": D ¿Has visto [la implementación] (http://www.facebook.com/video/video.php?v=1055625287695)? – edgerunner

+0

Creo que no se puede hacer en Ruby, pero dudo que la compilación tenga algo que ver con eso. Python tiene un patrón de diseño de clase base abstracto común utilizando la metaclase "ABCMeta", y no requiere compilación. –

+0

@SteveZelaznik: Justo, editado :) Lo importante es que admite la reapertura de clase y el código de carga en tiempo de ejecución, que * son * más fáciles en un entorno interpretado, pero, sí, los problemas no son imposibles. En cualquier caso, Python no admite la reapertura de clase, por lo que es gratis implementar clases abstractas :) – Matchu

1

No hay equivalente a clases abstractas en Ruby. Principalmente esto se debe a que Ruby está tipado dinámicamente, lo que significa que el concepto de clases abstractas no se aplica realmente.

2

Yo solo puedo usar métodos vacíos, o hacer que generen errores para forzar que las subclases los implementen.

class Base 
    def abstract_method 
    end 

    def mandatory_abstract_method 
    raise NotImplementedError.new("You must implement this") 
    end 
end 

El inconveniente es que esto solo lo hará cumplir cuando se llame realmente al método. Ruby es un lenguaje dinámico y no tiene ninguna verificación en tiempo de compilación.

19

Utilice un módulo que defina los métodos que deben implementarse.

module Animal 
    def eat 
    raise NotImplementedError 
    end 
end 

class Cat 
    include Animal 

    def eat 
    "Om nom nom" 
    end 
end 

class Dog 
    include Animal 
end 

c = Cat.new 
c.eat # => "Om nom nom" 

d = Dog.new 
d.eat # => NotImplementedError 
0

Si realmente desea implementar la abstracción, utilice la abstracción gema.

sudo gem install abstraction 

Ejemplo de referencia Ejemplo de Abstraction gem WIKI.

class Car 
    abstract 

    def go_forward 
    # ... 
end 
end 

Car.new 
> AbstractClassError: Car is an abstract class and cannot be instantiated 

class Convertible < Car 
    def door_count 
    2 
    end 
end 

class Sedan < Car 
    def door_count 
    4 
    end 
end 

Convertible.new # => #<Convertible:0x8fdf4> 
+0

Esto solo evita la creación de instancias de la clase abstracta. De ahora en adelante, garantiza que los métodos abstractos se implementen en las subclases concretas. –

Cuestiones relacionadas