ruby on rails - tutorial - Llamando a un método de subclase desde una superclase
ruby tutorial classes (4)
Prefacio: Esto está en el contexto de una aplicación Rails. La pregunta, sin embargo, es específica de Ruby.
Digamos que tengo un objeto Media
.
class Media < ActiveRecord::Base
end
Lo he extendido en unas pocas subclases:
class Image < Media
def show
# logic
end
end
class Video < Media
def show
# logic
end
end
Desde dentro de la clase Media
, quiero llamar a la implementación de show
desde la subclase apropiada. Entonces, desde Media, si self
es un Video
, llamaría el método show del video. Si el self
es en cambio una Image
, llamaría el método show de la imagen.
Proveniente de un fondo de Java, lo primero que me vino a la cabeza fue ''crear un método abstracto en la superclase''. Sin embargo, he leído en varios lugares (incluido el desbordamiento de pila ) que los métodos abstractos no son la mejor manera de lidiar con esto en Ruby.
Con eso en mente, comencé a investigar encasillados y descubrí que esto también es una reliquia de Java, que creo que debo eliminar de mi mente cuando trato con Ruby.
Derrotado, comencé a codificar algo que se veía así:
def superclass_method
# logic
this_media = self.type.constantize.find(self.id)
this_media.show
end
He estado codificando en Ruby / Rails por un tiempo, pero como era la primera vez que probaba este comportamiento y los recursos existentes no respondían directamente a mi pregunta, quería recibir comentarios de desarrolladores más experimentados sobre cómo lograrlo. mi tarea.
Entonces, ¿cómo puedo llamar a la implementación de un método de una subclase de la superclase en Rails? ¿Hay una forma mejor que la que terminé (casi) implementando?
Buena pregunta, pero lo estás haciendo muy complicado. Tenga en cuenta algunos principios y todo debe quedar claro ...
Los tipos se resolverán dinámicamente, por lo que si un
show
existe en cualquier lugar de la jerarquía de clases del objeto en el momento en que se llama , Ruby lo encontrará y lo llamará. Le invitamos a que escriba las llamadas de método a cualquier cosa que pueda o no exista en el futuro, y es una sintaxis legal de ruby que se analizará. Puede escribir una expresión que incluya una referencia athis_will_never_be_implemented
y a nadie le importará a menos que realmente sea llamado.Incluso en Java, solo hay un objeto real. Sí, puede tener un método en la superclase que está llamando a un método, pero es una instancia de la clase derivada (así como una instancia de la clase base) y, por lo tanto, puede contar con la nueva convocatoria.
En cierto sentido, cada clase de Ruby es una clase abstracta que contiene apéndices para cada método posible que pueda definirse en el futuro. Puede llamar a cualquier elemento sin calificadores de acceso en la clase base o en la clase derivada.
Si desea una implementación de superclase nula, puede definir una que no haga nada o que genere una excepción.
Actualización: Posiblemente, debería haber dicho "llamar a show
como cualquier otro método" y dejarlo así, pero habiendo llegado hasta aquí quiero agregar: También puede implementar el show
con la versión de herencia múltiple de Ruby: incluya SomeModule . Como obviamente estás interesado en el modelo de objetos de Ruby, puedes implementar tu atributo con una mezcla solo por diversión.
Como saben, tener una superclase sobre la funcionalidad de las subclases es un gran no-no, por lo que quería el método abstracto.
Lo que quieres hacer es definir show
en tu superclase. Luego puede llamarlo en la superclase y la subclase llamará a su propia versión, pero la superclase no generará un error.
class Media < ActiveRecord::Base
def show
# This method should be overloaded in a subclass
puts "Called from Media"
end
def do_something
show
end
end
class Image < Media
def show
puts "Called from Image"
end
end
class Video < Media
def show
puts "Called from Video"
end
end
i = Image.new
i.do_something
=> Called from Image
v = Video.new
v.do_something
=> Called from Video
Respuesta simple Sólo llámalo . Ruby no tiene una verificación en tiempo de compilación por lo que no hay nadie para quejarse de que el show
no esté definido en los Media
. Si @example
es una instancia de Image
, cualquier llamada a @example.show
se enviará a Image#show
primero, donde sea que se realice. Solo si no aparece Image#show
, la llamada se pasará a los Media
, incluso si la llamada se originó a partir del código definido en los Media
Si desea llamar a show
on self
desde un método de Media
, simplemente hágalo. Sin embargo, asegúrese de que self
responde a la llamada de método.
class Media < ActiveRecord::Base
def foo
if self.respond_to?(:show)
self.show
else
... // *
end
end
...
end
Para evitar la rama, implemente show
en Medios, usando * como el cuerpo de show
class Media < ActiveRecord::Base
def foo
self.show
end
def show
...
end
end