Паттерн «Наблюдатель»
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 44 из 57.
Паттерн наблюдатель позволяет объектам подписываться на изменения другого объекта — субъекта. Субъект хранит список наблюдателей и уведомляет каждого из них, когда что-то происходит, не зная, что они с этим делают:
class Thermometer
def initialize
@observers = []
end
def subscribe(observer)
@observers << observer
end
def temperature=(value)
@temperature = value
@observers.each { |o| o.update(value) }
end
end
class Display
def update(value)
puts "display: #{value} C"
end
end
class Alarm
def update(value)
puts "alarm: too hot!" if value > 30
end
end
t = Thermometer.new
t.subscribe(Display.new)
t.subscribe(Alarm.new)
t.temperature = 25
t.temperature = 35Вывод:
display: 25 C
display: 35 C
alarm: too hot!Наблюдателям нужен только метод update: утиная типизация. Каждый из них может быть совершенно другим классом, а новых наблюдателей можно добавлять, не изменяя субъект:
class Logger2
def update(value)
@history = (@history || []) << value
end
end
thermometer.subscribe(Logger2.new) # субъект не изменилсяНаблюдатели также могут быть блоками. Субъект сохраняет их как proc и вызывает с помощью call. unsubscribe снова удаляет наблюдателя:
class Button
def initialize
@handlers = []
end
def on_click(&handler)
@handlers << handler
handler
end
def unsubscribe(handler)
@handlers.delete(handler)
end
def click
@handlers.map { |h| h.call("clicked") }
end
end
b = Button.new
b.on_click { |e| "A got #{e}" }
second = b.on_click { |e| "B got #{e}" }
p b.click
b.unsubscribe(second)
p b.clickВывод:
["A got clicked", "B got clicked"]
["A got clicked"]Субъект решает, что отправить. Передача самого субъекта позволяет наблюдателю прочитать необходимые ему данные; передача имени события позволяет одному наблюдателю реагировать на несколько видов изменений:
@observers.each { |o| o.update(self) } # наблюдатель читает subject.price
@observers.each { |o| o.update(:sold_out, self) } # наблюдатель проверяет событиеЗадание
ЛегкоЗавершите класс Stock и двух наблюдателей. Stock.new(symbol) имеет методы subscribe(observer) и update_price(value); последний сохраняет цену и возвращает ненулевые ответы update(stock) каждого наблюдателя в порядке подписки. PriceDisplay#update возвращает AAPL now 12.50. PriceAlert.new(limit) возвращает ALERT AAPL above 100.00, когда цена поднимается выше лимита, будучи до этого равной ему или ниже, и nil в противном случае.
В первой строке ввода указаны символ и лимит оповещения. Предоставленный код подписывает отображение и оповещение, а затем выводит ответы для каждой строки set price.
Ваш код должен находиться в файлах stock.rb, price_display.rb и price_alert.rb. В main.rb находится предоставленный код ввода/вывода, и его нельзя изменять.
Попробуйте сами
require_relative 'stock'
require_relative 'price_display'
require_relative 'price_alert'
# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
input << line.chomp
end
symbol, limit = input[0].split
stock = Stock.new(symbol)
stock.subscribe(PriceDisplay.new)
stock.subscribe(PriceAlert.new(limit.to_f))
input.drop(1).each do |line|
stock.update_price(line.split[1].to_f).each { |reply| puts reply }
end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПеременные экземпляраМетоды и selfto_s и inspectПовторение — читательский билет4Инкапсуляция
Приватные методыЗащищённые методыПриватные сеттерыЗащита состоянияПовторение — торговый автомат7Полиморфизм и утиная типизация
ПолиморфизмУтиная типизацияАбстрактные методыcase и классыПовторение — способы оплаты10Шаблоны проектирования, часть 2
Делегирование с помощью ForwardableШаблон декоратораШаблон строителяШаблон состояния2Аксессоры и self
Чтение и записьПользовательские сеттерыself в методахИдентичность и равенствоПовторение — список контактов5Наследование
ПодклассыПереопределение методовВызов superis_a? и предкиПовторение — расчёт зарплаты сотрудников8Специальные методы
Равенство и хешированиеМетоды операторовИндексация и добавлениеПользовательские коллекцииПовторение — дроби11Проект: Управление библиотекой
Книги и читателиВыдача книг3Методы и переменные классов
Методы классовПеременные экземпляра классаКонстантыФабричные методыПовторение — коды купонов6Модули и примеси
Модули и includeРасширение объектовМодуль ComparableМодуль EnumerableПовторение — спортивная лига9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор Ruby