Every so often, it’s nice to give yourself a quick review of basic topics in software. Today, I wanted to review the observer design pattern and give a very simple implementation of it in Ruby. The observer pattern allows several observers to be notified when an observable object, or subject, changes its state. When the subject sends an update notification, the observers can act accordingly based on their type.
When to Observe
The observer pattern is useful for cases where an object is dependent on the state of another. The subject manages a list of zero or more observers made up of several types. Two common practical examples are:
- graphical interface elements of an application will change based on the state of back-end data
- a coupon subscription service will send notifications to several devices based on deals at different stores
We’ll start by implementing the
Observable module for our subject. Remember, the subject is responsible for managing and notifying several observers when its state changes.
Let’s break down the interesting parts:
These methods are used to manage the observers for our subject. We don’t want the same object in our list twice, so we only add an object if it isn’t already observing our subject.
When the subject changes its state, it needs to notify any object that is observing it. We overwrite the default behavior for
state=, which is a method created for us by attr_accessor. After reassigning the state value, we call
update on all the managed observers.
Here is what our
Observer module looks:
We leave the functionality of
update to the class including our module. This way, the Observer module behaves as an abstract class with an interface that needs to be implemented by the includer. This gives us the ability to include the Observer module into any class we want!
The Ant Colony
As a simple example, let’s create an ant colony using our modules. First, we define our subject, the Queen Ant:
Next, let’s define an observer:
WorkerAnt defines its own unique way of handling the state changes of the queen. Let’s create some worker ant objects to see how this works:
When the queen changes state, the
worker2 instances get notified and react accordingly. Since
worker3 was not attached, it does not get notified.
The queen can handle any type of observer, not just
WorkerAnts. Let’s create two additional observer classes:
Now, let’s create a soldier and breeder instance to observe the queen:
BreederAnt are different classes, but implement the same
Observer interface. When the queen changes her state, the two instances (
breeder) get notified and react accordingly.
There you have it! Please leave any questions, opinions, and/or corrections below in the comments.