Вызов метода подкласса из суперкласса
Предисловие: это в контексте приложения Rails. Вопрос, однако, специфичен для Ruby.
Скажем, у меня есть объект Media
.
class Media < ActiveRecord::Base
end
Я расширил его в нескольких подклассах:
class Image < Media
def show
# logic
end
end
class Video < Media
def show
# logic
end
end
Из класса Media
я хочу вызвать реализацию show
из соответствующего подкласса. Итак, из Media, если self
является Video
, тогда он будет вызывать метод Video show. Если self
вместо этого Image
, он будет вызывать метод отображения изображения.
Исходя из фона Java, первое, что появилось в моей голове, было "создать абстрактный метод в суперклассе". Тем не менее, я читал в нескольких местах (в том числе Stack Overflow), что абстрактные методы - это не лучший способ справиться с этим в Ruby.
С учетом этого, я начал исследование typecasting и обнаружил, что это также реликвия Java-мышления, которую мне нужно изгнать из моего разума при работе с Ruby.
Пораженный, я начал кодировать что-то похожее на это:
def superclass_method
# logic
this_media = self.type.constantize.find(self.id)
this_media.show
end
Я уже некоторое время программировал в Ruby/Rails, но так как это был мой первый раз, пытающийся выполнить это поведение, и существующие ресурсы не отвечали на мой вопрос напрямую, я хотел получить обратную связь от более опытных разработчиков на как выполнить мою задачу.
Итак, как я могу назвать реализацию подкласса метода из суперкласса в Rails? Есть ли лучший способ, чем то, что я закончил (почти) реализацию?
Ответы
Ответ 1
Хороший вопрос, но вы делаете это слишком сложно. Имейте в виду несколько принципов, и все должно быть ясно...
-
Типы будут разрешаться динамически, поэтому, если a show
существует в любом месте иерархии классов объектов, на данный момент это фактически называется, Ruby найдет его и вызовет. Вы можете вводить вызовы методов ко всему, что может или не может существовать в будущем, и это юридический синтаксис рубина, и он будет анализировать. Вы можете ввести выражение, которое включает ссылку на this_will_never_be_implemented
, и никто не будет заботиться, если он действительно не вызван.
-
Даже в Java существует только один фактический объект. Да, у вас может быть метод в суперклассе, который вызывает метод, но это экземпляр производного класса (а также экземпляр базового класса), и поэтому вы можете рассчитывать на вызов нового show
.
-
В некотором смысле каждый класс Ruby является абстрактным классом, содержащим заглушки для каждого возможного метода, который может быть определен в будущем. Вы можете вызывать что угодно без квалификаторов доступа в базовом классе или производном классе.
Если вы хотите реализовать нулевой суперкласс, вы можете определить тот, который ничего не делает или вызывает исключение.
Обновление: возможно, я должен был просто сказать "call show
как любой другой метод" и оставил его на этом, но, заходя так далеко, я хочу добавить: вы также можете реализовать show
с Ruby версией нескольких Наследование: включить SomeModule. Поскольку вы, очевидно, заинтересованы в объектной модели Ruby, вы можете реализовать свой атрибут с помощью mixin только для удовольствия.
Ответ 2
Как вы знаете, что суперкласс знает о функциональности подкласса, это большой нет-нет, поэтому вам нужен абстрактный метод.
Что вы хотите сделать, это определить show
в вашем суперклассе. Затем вы можете вызвать его в суперклассе, и подкласс вызовет его собственную версию, но суперкласс не будет вызывать ошибку.
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
Ответ 3
Если вы хотите вызвать show
on self
из метода Media
, просто сделайте это. Однако убедитесь, что self
отвечает на вызов метода.
class Media < ActiveRecord::Base
def foo
if self.respond_to?(:show)
self.show
else
... // *
end
end
...
end
Чтобы избежать ветки, реализуйте show
на носителе, используя * как тело show
class Media < ActiveRecord::Base
def foo
self.show
end
def show
...
end
end
Ответ 4
Простой ответ. Просто назовите его. Ruby не проверяет время компиляции, поэтому никто не должен жаловаться, что show
не определен на Media
. Если @example
является экземпляром Image
, тогда любой вызов @example.show
будет отправлен на Image#show
во-первых, где бы он ни был создан. Только если Image#show
не существует, то вызов будет передан на Media
, даже если вызов возник из кода, определенного в Media