Menu
Coddy logo textTech

תבנית Observer

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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)   # האובייקט הנצפה לא השתנה

גם צופים יכולים להיות בלוקים. הנושא שומר אותם כ-procs וקורא להם באמצעות 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 בודק את האירוע
challenge icon

אתגר

קל

השלם את המחלקה Stock ושני צופים. למחלקה Stock.new(symbol) יש את subscribe(observer) ואת update_price(value), ששומרת את המחיר ומחזירה את התשובות שאינן nil של update(stock) מכל צופה, לפי סדר ההרשמה. PriceDisplay#update מחזירה את AAPL now 12.50. PriceAlert.new(limit) מחזירה את ALERT AAPL above 100.00 כשהמחיר עולה מעל המגבלה לאחר שהיה שווה לה או נמוך ממנה, ואת nil אחרת.

שורת הקלט הראשונה מכילה את הסימול ואת מגבלת ההתראה. הקוד שסופק רושם צג והתראה, ואז מדפיס את התשובות עבור כל שורת מחיר.

הקוד שלך צריך להופיע בקבצים 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
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Ruby אונליין