Mẫu Observer
Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 44 trên 57.
Mẫu observer cho phép các đối tượng đăng ký nhận thông báo về những thay đổi của một đối tượng khác, gọi là subject. Subject lưu danh sách các observer và thông báo cho từng observer khi có sự việc xảy ra, mà không cần biết chúng sẽ xử lý ra sao:
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Đầu ra:
display: 25 C
display: 35 C
alarm: too hot!Các observer chỉ cần một phương thức update: duck typing. Mỗi observer có thể là một lớp hoàn toàn khác, và bạn có thể thêm observer mới mà không cần thay đổi subject:
class Logger2
def update(value)
@history = (@history || []) << value
end
end
thermometer.subscribe(Logger2.new) # đối tượng chủ thể không thay đổiCác observer cũng có thể là khối lệnh. Đối tượng subject lưu chúng dưới dạng proc và gọi chúng bằng call. unsubscribe xóa observer:
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Đầu ra:
["A got clicked", "B got clicked"]
["A got clicked"]Chủ thể (subject) quyết định nội dung cần gửi. Truyền chính chủ thể đó cho phép đối tượng quan sát (observer) đọc thông tin cần thiết; truyền tên sự kiện cho phép một đối tượng quan sát phản ứng với nhiều loại thay đổi:
@observers.each { |o| o.update(self) } # Bộ quan sát đọc subject.price
@observers.each { |o| o.update(:sold_out, self) } # Bộ quan sát kiểm tra sự kiện (event)Thử thách
DễHoàn thiện đối tượng Stock và hai observer. Stock.new(symbol) có subscribe(observer) và update_price(value), phương thức này lưu giá và trả về các phản hồi khác nil từ update(stock) của từng observer, theo thứ tự đăng ký. PriceDisplay#update trả về AAPL now 12.50. PriceAlert.new(limit) trả về ALERT AAPL above 100.00 khi giá tăng vượt giới hạn từ mức bằng hoặc thấp hơn giới hạn, và trả về nil trong các trường hợp khác.
Dòng đầu tiên của đầu vào là mã cổ phiếu và giới hạn cảnh báo. Code được cung cấp đăng ký một màn hình hiển thị và một cảnh báo, sau đó in ra các phản hồi cho từng dòng set price.
Code của bạn nằm trong stock.rb, price_display.rb và price_alert.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.
Tự làm thử
require_relative 'stock'
require_relative 'price_display'
require_relative 'price_alert'
# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
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
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpBiến instancePhương thức và selfto_s và inspectÔn tập: thẻ thư viện4Tính đóng gói
Phương thức riêng tưPhương thức được bảo vệPhương thức thiết lập riêng tưBảo vệ trạng tháiÔn tập: máy bán hàng tự động7Tính đa hình và duck typing
Tính đa hìnhDuck typingPhương thức trừu tượngcase và lớpÔn tập: phương thức thanh toán2Phương thức truy cập và self
Phương thức đọc và ghiPhương thức gán tùy chỉnhself trong phương thứcĐịnh danh và tính bằng nhauÔn tập: Danh sách liên hệTự luyện tập: Trình biên dịch Ruby online