~ The same as Rails callback, that we can do notify others in the callback
Fred gets a raise and his Employee record shouts out to the world (or at least to anyone who seems interested), “Hello! I’ve got something going on here!”. Any object that is interested in the state of Fred’s finances need simply register with his Employee object ahead of time. Once registered, that object would receive timely updates about the ups and downs of Fred’s paycheck.
class Payroll
def update( changed_employee )
puts("Cut a new check for #{changed_employee.name}!")
puts("His salary is now #{changed_employee.salary}!")
end
end
class Employee
attr_reader :name
attr_accessor :title, :salary
def initialize( name, title, salary, payroll)
@name = name
@title = title
@salary = salary
@payroll = payroll
end
end
Because the salary field accessible with attr_accessor, our employees can get raises:
payroll = Payroll.new
fred = Employee.new("Fred Flintstone", "Crane Operator", 30000.0, payroll)
# Give Fred a raise
fred.salary = 35000.0
Then, how can we let the payroll that the Employee record had changed the salary (e.g. Fred has a raise in his salary)
# Employee model
def salary=(new_salary)
@salary = new_salary
**@payroll.update(self)**
end
Perhaps we should step back and try to solve this notification problem in a more general way. How can we separate out the thing that is changing—who gets the news about salary changes—from the real guts of the Employee object? We can set up an array for just that purpose in the initialize method:
def initialize( name, title, salary )
@name = name
@title = title
@salary = salary
@observers = []
end
def salary=(new_salary)
@salary = new_salary
notify_observers
end
def notify_observers
@observers.each do |observer|
observer.update(self)
end
end
The only job left is to write the methods that add and delete observers from the Employee object:
def add_observer(observer)
@observers << observer
end
def delete_observer(observer)
@observers.delete(observer)
end
The key decisions that you need to make when implementing the Observer pattern all center on the interface between the subject and the observer. At the simple end of the spectrum, you might do what we did in the example above: Just have a single method in the observer whose only argument is the subject. The GoF term for this strategy is the pull method, because the observers have to pull whatever details about the change that they need out of the subject. The other possibility—logically enough termed the push method—has the subject send the observers a lot of details about the change:
observer.update(self, :salary_changed, old_salary, new_salary)
We can even define different update methods for different events. For example, we could have one method for a salary update or title change
observer.update_salary(self, old_salary, new_salary)
observer.update_title(self, old_title, new_title)
The advantage in providing more details is that the observers do not have to work quite as hard to keep track of what is going on. The disadvantage of the push model is that if all of the observers are not interested in all of the details, then the work of passing the data around goes for naught.