Подтвердить что ты не робот

Вызов метода подкласса из суперкласса

Предисловие: это в контексте приложения 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? Есть ли лучший способ, чем то, что я закончил (почти) реализацию?

4b9b3361

Ответ 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