Note: We won’t be going over the Ruby module Observable. Instead, we’ll building out the pattern ourselves.

Your First Day at the NSA

Welcome to the National Security Agency, Agent Smith. You have quite an impressive background, and we believe your “go-getter” attitude will instill a new kind of vigor within the organization.

Your cubicle is down to the left… here are some NDAs for you to fill out. I’ll swing by your desk in the afternoon and pick them up from you later. Oh, and before I forget, here is your first assignment.

Go get ‘em, tiger!

The First Assignment

Agent Smith Spook First Class [REDACTED] NSA 08-20-[REDACTED] Operation [REDACTED] Observers Welcome, Agent Smith: Bluntly, we'd like to track everyone's emails. Attached are two documents. The first document will show you the basic structure of a typical email, and the second document will provide you a basic profile of a suspicious person. If there are any questions, please reach me at [REDACTED]. Best of luck, Agent [REDACTED] [REDACTED] [REDACTED] NSA

# Document 1: # Basic structure of an email module Email extend self def send(subject, sender, receiver) puts %Q[ Subject: #{subject} From: #{sender}@example.com To: #{receiver}@example.com Date: #{Time.now.asctime} ] end end

# Document 2: # Characteristics of a suspicious person class Person include Email attr_reader :name def initialize(name) @name = name end def send_email(subject, receiver) Email.send(subject, name, receiver) end end

As we look through the Email module, we see that it contains Email.send which takes three arguments: subject , sender , and receiver .

Gazing at the suspicious Person class, we see that it includes the Email module. Person#send_email takes two parameters: a subject and a receiver. Person#name will stand in as the sender of the email.

Hypothetically, let’s see how a suspicious person would send an email:

bill = Person.new 'Bill' bill.send_email 'Fishing Trip', 'Fred' # => Subject: Fishing Trip From: Bill@example.com To: Fred@example.com Date: Wed Aug 16 20:35:09 2006

Hmm… as you sit in your cubicle, you ponder the numerous possible ways of tracking emails. You won’t need anything too complicated, just something to kick off a notification once an email has been sent.

Volia! You realize you can use the Observer pattern!

The Subject and its Observers

First, let’s start off by creating two observer classes, Alert and Agent classes.

class Alert def gotcha(person) puts "!!! ALERT: #{person.name.upcase} SENT AN EMAIL !!!" end end class Agent def gotcha(person) puts "!!! TIME TO DETAIN #{person.name.upcase} !!!" end end

Next, let’s create a Subject module.

module Subject attr_reader :observers def initialize @observers = [] end def add_observer(*observers) observers.each { |observer| @observers << observer } end def delete_observer(*observers) observers.each { |observer| @observers.delete(observer) } end private def notify_observers observers.each { |observer| observer.gotcha(self) } end end

Here within the Subject#initialize , we create an empty array which will contain a list of observers. Subject#add_observer simply pushes our desired observers into the array.

Finally, we can alter the suspicious Person class, which will act as the subject class. Let’s include the Subject module now.

class Person include Email, Subject attr_reader :name def initialize(name) # 'super' requires a parentheses because we're calling # super on the superclass, 'Subject' super() @name = name end def send_email(subject, receiver) Email.send(subject, name, receiver) notify_observers end end

Subject#notify_observers calls #gotcha on each observer, which informs each observer that Person#send_email has been kicked off.

Now let’s give it a whirl…

alert = Alert.new agent = Agent.new bill = Person.new 'Bill' bill.add_observer alert, agent # Bill now has two observers watching him bill.send_email 'Fishing Trip', 'Fred' # => Subject: Fishing Trip From: Bill@example.com To: Fred@example.com Date: Wed Aug 16 20:35:09 2006 !!! ALERT: BILL SENT AN EMAIL !!! !!! TIME TO DETAIN BILL !!!

Perfect, it works! Now we can start protecting our freedom!

Discussion

In our example above, we have two observers, the Alert and Agent classes, and a subject, Person . By creating the Subject module, any instance of Person now informs and updates any observer through #notify_observers , ultimately removing any implicit coupling from Alert and Agent .

There are a few similarities between the Observer and Strategy patterns. Both patterns employ an object (the Observer’s subject and the Strategy’s context) that makes calls to another object (the Observer’s observer or Strategy’s strategy). The difference between the two patterns is the purpose and use case. The Strategy pattern relies on the strategy to do the work, while the Observer pattern informs the observers of what is going on with the subject.

Hope you enjoyed this short example, thanks for reading!