Home > Articles > Programming > General Programming/Other Languages

Design Patterns in Ruby: Keeping Up with the Times with the Observer Pattern

  • Print
  • + Share This
The Observer pattern allows you to build components that know about the activities of other components without having to tightly couple everything together in an unmanageable mess of code-flavored spaghetti.
This chapter is from the book

This chapter is from the book

One of the knottiest design challenges is the problem of building a system that is highly integrated—that is, a system where every part is aware of the state of the whole. 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. Even more simply, how about a personnel system that needs to let the payroll department know when someone's salary changes?

Building this kind of system is hard enough, but throw in the requirement that the system be maintainable and now you are talking truly difficult. How can you tie the disparate parts of a large software system together without increasing the coupling between classes to the point where the whole thing becomes a tangled mess? How do you construct a spreadsheet that properly displays changes in the data without hard-coding a link between the spreadsheet editing code and the bar chart renderer? How can you make the Employee object spread the news about salary changes without tangling it up with the payroll system?

Staying Informed

One way to solve this problem is to focus on the fact that the spreadsheet cell and the Employee object are both acting as a source of news. 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.

How would all of this work in code? Here is a basic version of an Employee object with no code to tell anyone anything—it just goes about its business of keeping track of an employee:

class Employee
 attr_reader :name
 attr_accessor :title, :salary

 def initialize( name, title, salary )
   @name = name
   @title = title
   @salary = salary
 end
end

Because we have made the salary field accessible with attr_accessor, our employees can get raises:1

fred = Employee.new("Fred Flintstone", "Crane Operator", 30000.0)

# Give Fred a raise

fred.salary=35000.0

Let's now add some fairly naive code to keep the payroll department informed of pay changes:

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, :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

We can now change Fred's wages:

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

And the payroll department will know all about it:

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

Note that since we need to inform the payroll department about changes in salary, we cannot use attr_accessor for the salary field. Instead, we need to write the salary= method out by hand.

  • + Share This
  • 🔖 Save To Your Account