GOF [Gang of Four] : Observer Design pattern in Ruby

Overview

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems. The Observer pattern is also a key part in the familiar model–view–controller (MVC) architectural pattern.[1] The observer pattern is implemented in numerous programming libraries and systems, including almost all GUI toolkits.

Problems Observer Pattern Solves:

  • Think about a spreadsheet, where editing the contents of one cell not only changes the number in the grid but also changes the column totals, alters the height of one of the bars in a bar chart, and enables the Save button
  •  a personnel system that needs to let the payroll department know when someone’s salary changes.How can you make the Employee object spread the news about salary changes without tangling it up with the payroll system?

Solving Employee Salary problem with simple technique

class Employee
  attr_reader :name, :title
  attr_reader :salary
  def initialize(name, title, salary, payroll)
    @name = name 
    @title = title
    @salary = salary
    @payroll = payroll
  end
  def salary=(new_salary)
    @salary = new_salary
    @payroll.update(self)
  end
end

Payroll class be like

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

We can now change Fred’s wages:

payroll = Payroll.new 
fred = Employee.new('Fred', 'Crane Operator', 30000, payrollfred.salary = 35000

And the payroll department will know all about it:

Cut a new check for Fred! 
His salary is now 35000!

Problem with this solution:

The trouble with this code is that it is hard-wired to inform the payroll department about salary changes.

  • What do we do if we need to keep other objects—perhaps some accounting-related classes—informed about Fred’s financial state?
    • As the code stands right now, we must go back in and modify the Employee class.

A Better way to do that : Using Observer Pattern

class Employee
  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
  def add_observer(observer)
    @observers << observer
  end
  def delete_observer(observer)
    @observers.delete(observer)
  end
end

Now any object that is interested in hearing about changes in Fred’s salary can simply register as an observer on Fred’s Employee object:

fred = Employee.new('Fred', 'Crane Operator', 30000.0)
payroll = Payroll.new 
fred.add_observer( payroll )
fred.salary = 35000.0
 
# output
Cut a new check for Fred! 
His salary is now 80000.0!

By building this general mechanism, we have removed the implicit coupling between the Employee class and the Payroll object.

  • Employee no longer cares which or how many other objects are interested in knowing about salary changes;
    • it just forwards the news to any object that said that it was interested.
  • In addition, instances of the Employee class will be happy with no observers, one, or several embedded hard-wired inside it:
class TaxMan
  def update(changed_employee)
    puts("Send #{changed_employee.name} a new tax bill!")
  end
end
 
tax_man = TaxMan.new 
fred.add_observer(tax_man)
 
# Suppose we change Fred’s salary again:
fred.salary = 90000.0

Now both the payroll department and the tax man will hear about it:

# Output
Cut a new check for Fred!
His salary is now 80000.0!
Send Fred a new tax bill!

Conclusion:

The GoF called this idea of building a clean interface between the source of the news that some object has changed and the consumers of that news the Observer pattern (Figure 5-1).

  • The GoF called the class with the news the subject class. In our example, the subject is the Employee class.
  • The observers are the objects that are interested in getting the news.
  • In our employee example, we have two observers: Payroll and TaxMan.
  • When an object is interested in being informed of the state of the subject, it registers as an observer on that subject.

This document contains just the Introduction to Observer Pattern: You can explore more and update/extend this document further

References:

Book: Design Patterns in Ruby – Russ Olsen

https://en.wikipedia.org/wiki/Observer_pattern

https://github.com/rails/rails-observers

One thought on “GOF [Gang of Four] : Observer Design pattern in Ruby

Leave a comment