This post is part of a series of reviews on the book Design Patterns in Ruby. Check out the Introduction post for a full table of contents along with some generic principles regarding Design Patterns.

The Observer looks alot like the Strategy in terms on code and organization, but these two patterns vary alot when it comes to their intent. The focus here is to stay informed about, or observe, a certain object.

A cool example can be seen in a context of an employee of a company. Different sections of the company need to stay aware of an employee’s current salary, for example, so we apply the Observer pattern:

To the code, starting with the Employee class:

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

I didn’t use an attr_accessor on salary because then we wouldn’t be able to inform anybody when it changed. We’re making use of delegation to call a method on a referenced object, exactly like we did on Strategy. Also notice we’re calling update on a payroll object, so let’s create his class:

class Payroll
	def update(employee)
		puts("New check for #{employee.name}, his salary is now #{employee.salary}.")
	end
end

What’s happening here is that we’re letting the Payroll class stay informed of what’s happening over at Employee. Although I used a simple puts to say it changed, we could easily put a database transaction in there or some other important business logic.
Let’s see the Observer in action:

pedro = Employee.new('Pedro',  10000, Payroll.new)
pedro.salary = 20000

Of course this first example is only taking into consideration that we have one department, what would happen if we had 10? We’d pass all 10 in the constructor? No, we’d hold an observers list:

class Employee
	attr_reader :name, :salary
	
	def initialize(name, salary)
		@name = name
		@salary = salary
		@observers = []
	end
	
	def salary=(new_salary)
		@salary = new_salary
		notify_observers
	end
	
	def add_observer(observer)
		@observers << observer
	end
	
	def delete_observer(observer)
		@observer.delete(observer)
	end
	
	def notify_observers
		@observers.each{|obs| obs.update(self)}
	end
end
	
class Payroll
	def update(employee)
		puts("New check for #{employee.name}, his salary is now #{employee.salary}.")
	end
end

class TaxMan
	def update(employee)
		puts("Send #{employee.name} a new tax bill!")
	end
end


pedro = Employee.new('Pedro',  10000)

pedro.add_observer(Payroll.new)
pedro.add_observer(TaxMan.new)

pedro.salary = 20000

Although it works for now, once we have more subjects that need to store and notify observers, the code will get duplicated, and duplicated code is never good.
We could extract out the add/remove_observer and notify_observers methods to a Subject class and make Employee subclass it. But why bother? By doing that we’d be subclassing something just to reuse code. And that’s where Ruby modules come in to the rescue.

Ruby natively supports the Observer pattern by supplying the Observable module which gives us all that observer managing code for free:

require 'observer'
class Employee
	include Observable
	attr_reader :name, :salary
	
	def initialize(name, salary)
		@name = name
		@salary = salary
	end
	
	def salary=(new_salary)
		@salary = new_salary
		changed
		notify_observers(self)
	end
end

Similar to what we did in the Strategy pattern, we can use code blocks to enhance our experience with the Observer, something commonly used by GUI frameworks:

class Employee
	attr_reader :name, :salary
	
	def initialize(name, salary)
		@name = name
		@salary = salary
		@observers = []
	end
	
	def salary=(new_salary)
		@salary = new_salary
		notify_observers
	end
	
	def add_observer(&observer)
		@observers << observer
	end
	
	def delete_observer(observer)
		@observer.delete(observer)
	end
	
	def notify_observers
		@observers.each{|obs| obs.call(self)}
	end
end
	
pedro = Employee.new('Pedro',  10000)

pedro.add_observer do |subject|
	puts("New check for #{subject.name}, his salary is now #{subject.salary}.")
end

pedro.add_observer do |subject|
	puts("Send #{subject.name} a new tax bill!")
end

pedro.salary = 20000

Just like on Strategy, the decision between choosing the block way or the array way is entirely up to your needs.