Design patterns in life and Ruby — gain an intuitive understanding of OO design patterns by linking them with real-life examples.
If you listen to podcasts, you are already familiar with the Observer pattern. In fact, you are an “observer”.
Here’s the definition for the Observer pattern:
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.
Let’s look at the definition as related to podcasts.
I find an interesting podcast named developer tea
.
After clicking the SUBSCRIBE
button, I'm now on their subscriber list.
When developer tea
releases a new episode, the Podcasts app will notify me and other subscribers and automatically download the new episode for us.
That’s exactly the definition of the Observer pattern!
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.
There is a one-to-many relationship between the developer tea
podcast and subscribers
. When developer tea
chanes state, such as releasing a new episode, all of developer tea
's subscribers
are notified and updated automatically.
Let’s implement it in Ruby.
Start with a simple version.
class Podcast attr_reader :episodes def initialize() @episodes = [] end def add_episode(new_episode) episodes << new_episode end end
The Podcast
class holds a list of episodes and has a method to add_episode
to the list.
Then we can create the developer_tea
podcast and add episode #1 to it like this:
I want to get a notification whenever a new episode is released.
We can simply update
me after adding a new episode to the list:
class Podcast attr_reader :episodes def initialize() @episodes = [] end def add_episode(new_episode, sihui) episodes << new_episode sihui.update(self) end end
And whenever I get an update from developer_tea
, I can go ahead and download the latest episode.
I enjoy listening to developer_tea
so much that I recommend it to my friend, Amber. Now, Amber wants to subscribe to it as well.
We need to make sure Amber also gets a notification whenever a new episode is released:
class Podcast attr_reader :episodes def initialize() @episodes = [] end def add_episode(new_episode, sihui, amber) episodes << new_episode sihui.update(self) amber.update(self) end end
Hmmm, now, the code does what we want.
But there is a problem.
Each time we want to add a subscriber, we have to redefine the class.
Is there a way to update the subscriber list dynamically without having to redefine the class?
We can keep a subscriber list!
class Podcast attr_reader :episodes, :subscribers def initialize() @episodes = [] @subscribers = [] end def add_subscriber(new_subscriber) subscribers << new_subscriber end def remove_subscriber(party_pooper) subscribers.delete(party_pooper) end def add_episode(new_episode) episodes << new_episode subscribers.each do |subscriber| subscriber.update(self) end end end
The new Podcast
class keeps a subscriber list with the help of two new methods: one for adding subscribers and one for removing subscribers. When an episode is released, we update each subscriber.
Unfortunately, Amber doesn’t enjoy the podcast as much as I do and decides to unsubscribe. We use the remove_subscriber
method to remove her from the subscriber list.
Yay! You learn the Observer pattern!
A Design Principle Behind The Observer Pattern.
The Observer pattern utilizes the Loose Coupling design principle:
Strive for loosely coupled designs between objects that interact.
The Podcast
class doesn’t know much about its subscribers besides the fact that each subscriber has an update
method. This loose coupling minimizes the dependency between Podcast
and its subscribers and maximizes flexibility. As long as it has an update
method, a subscriber
can be anything: a human, a group of people, an animal, or even a car.
Takeaways:
- The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.
- Loose Coupling design principle: strive for loosely coupled designs between objects that interact.
Thanks for reading. Are there any other real-life examples of the Observer pattern you can think of?
Don't forget to subscribe. Next time we will talk about…
Enjoyed the article?
My best content on Software Design, Rails, and Career in Dev. Delivered weekly.
How do you find these real world examples? Sihui. It’s pretty easy to understand.
Hi Bhanu, I’m glad you find it easy to understand. I found these examples just by thinking about the concept and try to draw analogies.