옵서버 패턴
Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 44번째.
옵저버 패턴을 사용하면 객체가 다른 객체인 주체의 변경 사항을 구독할 수 있습니다. 주체는 옵저버 목록을 유지하고, 어떤 일이 발생하면 각 옵저버가 이를 어떻게 처리하는지 알지 못한 채 모두에게 알립니다:
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) } # observer가 subject.price를 읽음
@observers.each { |o| o.update(:sold_out, self) } # observer가 이벤트를 확인함챌린지
쉬움Stock 주체와 두 옵저버를 완성하세요. Stock.new(symbol)에는 subscribe(observer)와 update_price(value)가 있으며, 후자는 가격을 저장하고 모든 옵저버에 대해 구독 순서대로 update(stock)의 nil이 아닌 응답을 반환합니다. 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
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 Ruby 컴파일러