Композиция отражения 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 ответа
На мой взгляд, этот подход слишком сложен и нецелесообразно использовать Module
s.
Я рекомендую подумать о более простом способе реализации этого.
Один простой способ - просто включить все методы в класс 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
вызвать иерархию наследования.