نمط 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) } # المراقب يقرأ subject.price
@observers.each { |o| o.update(:sold_out, self) } # المراقب يتحقق من الحدثالتحدي
سهلأكمل الصنف 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 بخلاف ذلك.
يحتوي سطر الإدخال الأول على الرمز وحد التنبيه. يشترك الكود الموفَّر في عرض وتنبيه، ثم يطبع الردود لكل سطر 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مراجعة - بطاقة المكتبة7تعدد الأشكال والكتابة بأسلوب البطة
تعدد الأشكالالكتابة بأسلوب البطةالأساليب المجردةالحالات والفئاتمراجعة - طرق الدفع3أساليب ومتغيرات الأصناف
أساليب الأصنافمتغيرات كائنات الأصنافالثوابتأساليب الإنشاءمراجعة - رموز القسائمتدرّب بنفسك: مترجم Ruby عبر الإنترنت