Композиция отражения Ruby: вызов исходного метода из переопределенного метода

Сначала немного контекста

У меня есть класс Phone который определяет метод advertise как это:

class Phone
  def advertise(phone_call)
    'ringtone'
  end
end

Я хотел бы иметь некоторые адаптации для этого метода. Например, когда пользователь находится в тихой обстановке, телефон должен вибрировать, а не звонить. Для этого я определяю модули как

module DiscreetPhone    
  def advertise_quietly (phone_call)
    'vibrator'
  end
end

Тогда моя программа может сделать

# add the module to the class so that we can redefine the method
Phone.include(DiscreetPhone) 
# redefine the method with its adaptation
Phone.send(:define_method, :advertise, DiscreetPhone.instance_method(:advertise_quietly ))

Конечно, для этого примера я жестко закодировал имя класса и модуля, но они должны быть параметрами функции.

И так, пример выполнения даст:

phone = Phone.new
phone.advertise(a_call) # -> 'ringtone'
# do some adaptation stuff to redefine the method
...
phone.advertise(a_call) # -> 'vibrator'

Наконец подходит к моему вопросу

Я хочу иметь адаптацию, которая вызывает исходную функцию и добавляет что-то к ее результату. Я хотел бы написать это как

module ScreeningPhone
  def advertise_with_screening (phone_call)
    proceed + ' with screening'
  end
end

Но я не знаю, что proceed вызов должен сделать или даже где я должен определить это.

  • Я использую Ruby 2.3.0 на Windows.
  • proceed может быть заменено чем-то другим, но я бы хотел сохранить его как можно более чистым в модуле, который определяет адаптацию.

2 ответа

Решение

На мой взгляд, этот подход слишком сложен и нецелесообразно использовать Modules.

Я рекомендую подумать о более простом способе реализации этого.

Один простой способ - просто включить все методы в класс Phone.

Или вы можете использовать хеш в качестве справочной таблицы для кольцевых стратегий:

class Phone

    attr_accessor :ring_strategy

    RING_STRATEGIES = {
        ringtone:  -> { ring_with_tone },
        discreet:  -> { ring_quietly },
        screening: -> { ring_with_tone; ring_screening_too }
        # ...
    }

    def initialize(ring_strategy = :ringtone)
        @ring_strategy = ring_strategy
    end

    def ring
        RING_STRATEGIES[:ring_strategy].()
    end

end

Вы можете сделать это, добавив модуль вместо того, чтобы включать его.

Вместо того, чтобы использовать define_method как своего рода эрзац alias_methodпросто вызовите метод advertise в ваших модулях тоже.

В вашем advertise метод, вы можете позвонить super вызвать иерархию наследования.

Другие вопросы по тегам