Sé que no existe el concepto de clase abstracta en ruby. Pero si es necesario implementarlo, ¿cómo hacerlo? Intenté algo como ...
class A
def self.new
raise 'Doh! You are trying to write Java in Ruby!'
end
end
class B < A
...
...
end
Pero cuando intento crear una instancia de B, va a llamar internamente A.new
cuál va a generar la excepción.
Además, los módulos no se pueden instanciar, pero tampoco se pueden heredar. hacer que el nuevo método sea privado tampoco funcionará. ¿Algún consejo?
ruby
abstract-class
Chirantan
fuente
fuente
raise "Doh! You are trying to write Java in Ruby"
.Respuestas:
No me gusta usar clases abstractas en Ruby (casi siempre hay una forma mejor). Sin embargo, si realmente cree que es la mejor técnica para la situación, puede usar el siguiente fragmento para ser más explícito sobre qué métodos son abstractos:
Básicamente, solo llama
abstract_methods
con la lista de métodos que son abstractos, y cuando son llamados por una instancia de la clase abstracta, seNotImplementedError
generará una excepción.fuente
NotImplementedError
que esencialmente significa "dependiente de la plataforma, no disponible en la suya". Ver documentos .Solo para intervenir tarde aquí, creo que no hay razón para evitar que alguien cree una instancia de la clase abstracta, especialmente porque pueden agregarle métodos sobre la marcha .
Los lenguajes de tipificación de pato, como Ruby, utilizan la presencia / ausencia o el comportamiento de métodos en tiempo de ejecución para determinar si deben llamarse o no. Por lo tanto, su pregunta, ya que se aplica a un método abstracto , tiene sentido.
y eso debería ser el final de la historia. La única razón para usar clases abstractas en Java es insistir en que ciertos métodos se "completen" mientras que otros tienen su comportamiento en la clase abstracta. En un lenguaje de tipeo de pato, el enfoque está en los métodos, no en las clases / tipos, por lo que debe mover sus preocupaciones a ese nivel.
En su pregunta, básicamente está tratando de recrear la
abstract
palabra clave de Java, que es un olor a código para hacer Java en Ruby.fuente
Prueba esto:
fuente
#initialize
de B, en realidad puede generar lo que sea en A # initializeif self.class == A
.fuente
para cualquiera en el mundo de los rieles, la implementación de un modelo ActiveRecord como una clase abstracta se realiza con esta declaración en el archivo del modelo:
fuente
Mi 2 ¢: opto por una mezcla DSL simple y liviana:
Y, por supuesto, agregar otro error para inicializar la clase base sería trivial en este caso.
fuente
err = NotImplementedError.new(message); err.set_backtrace caller()
YMMVEn los últimos 6 años y medio de programar Ruby, no he necesitado una clase abstracta ni una sola vez.
Si está pensando que necesita una clase abstracta, está pensando demasiado en un lenguaje que las proporciona / requiere, no en Ruby como tal.
Como han sugerido otros, un mixin es más apropiado para cosas que se supone que son interfaces (como las define Java), y repensar su diseño es más apropiado para cosas que "necesitan" clases abstractas de otros lenguajes como C ++.
Actualización 2019: No he necesitado clases abstractas en Ruby en 16 años y medio de uso. Todo lo que dicen todas las personas que comentan en mi respuesta se aborda aprendiendo realmente Ruby y usando las herramientas apropiadas, como módulos (que incluso le brindan implementaciones comunes). Hay personas en los equipos que he administrado que han creado clases que tienen una implementación base que falla (como una clase abstracta), pero en su mayoría son un desperdicio de codificación porque
NoMethodError
producirían exactamente el mismo resultado que unaAbstractClassError
en producción.fuente
Puede probar 3 rubygems:
interfaz
abstracto
simple abstracto
fuente
Personalmente planteo NotImplementedError en métodos de clases abstractas. Pero es posible que desee dejarlo fuera del método "nuevo", por las razones que mencionó.
fuente
initialize
métodos no se llaman automáticamente a menos que se llamen explícitamente con super.Si desea ir con una clase no desinstalable, en su método A.new, verifique si self == A antes de lanzar el error.
Pero en realidad, un módulo se parece más a lo que quiere aquí; por ejemplo, Enumerable es el tipo de cosa que podría ser una clase abstracta en otros lenguajes. Técnicamente, no puede subclasificarlos, pero llamar
include SomeModule
logra aproximadamente el mismo objetivo. ¿Hay alguna razón por la que esto no funcione para usted?fuente
¿Qué propósito estás tratando de cumplir con una clase abstracta? Probablemente haya una mejor manera de hacerlo en ruby, pero no dio ningún detalle.
Mi puntero es este; utilizar un mixin no herencia.
fuente
Otra respuesta:
Esto se basa en el #method_missing normal para informar métodos no implementados, pero evita que se implementen clases abstractas (incluso si tienen un método de inicialización)
Como han dicho los otros carteles, probablemente debería usar una mezcla, en lugar de una clase abstracta.
fuente
Lo hice de esta manera, por lo que redefine lo nuevo en la clase secundaria para encontrar una nueva en la clase no abstracta. Todavía no veo nada práctico en el uso de clases abstractas en ruby.
fuente
También está esta pequeña
abstract_type
joya, que permite declarar clases y módulos abstractos de forma discreta.Ejemplo (del archivo README.md ):
fuente
No hay nada de malo en tu enfoque. Generar un error en la inicialización parece estar bien, siempre y cuando todas sus subclases anulen la inicialización, por supuesto. Pero no quieres definirte a ti mismo. Nuevo así. Esto es lo que haría.
Otro enfoque sería poner toda esa funcionalidad en un módulo, que, como mencionaste, nunca se podrá implementar. Luego incluya el módulo en sus clases en lugar de heredarlo de otra clase. Sin embargo, esto rompería cosas como super.
Entonces depende de cómo quieras estructurarlo. Aunque los módulos parecen una solución más limpia para resolver el problema de "¿Cómo escribo algunas cosas que están diseñadas para que las utilicen otras clases?"
fuente
Gema de 2 líneas: https://rubygems.org/gems/abstract
fuente
Aunque esto no se siente como Ruby, puedes hacer esto:
Los resultados:
fuente