model-view-controller architectural pattern.
In a traditional MVC ( Model-View-Controller ) architecture, a model is a subject and a view is an observer. A view is notified when a model changes and responds accordingly. When the subject sends observers detailed information about what has changed, indiscriminately, this is known as the push model of the Observer pattern. When a subject sends only minimal information to its observers must ask for details explicitly, this is known as the pull model of the Observer pattern.
Ruby provides a simple mechanism to implement this design pattern using the Observable
module. In this mechanism, the Notifier class uses the Observable
module, which provides the methods for managing the associated observer objects.
The observable object must:
- assert that it has
#changed
- call
#notify_observers
Public Instance Methods
Instance methods are methods that are called on an instance of a class. We can use the below methods while using Observer instances.- add_observer(observer, func=:update)
Adds observer
as an observer on this object, so that it will receive notifications.
- changed(state=true)
Set the changed state of this object. Notifications will be sent only if the changed state
is true
.
- changed?()
Returns true if this object’s state has been changed since the last notify_observers call.
- count_observers()
Return the number of observers associated with this object.
- delete_observer(observer)
Remove observer
as an observer on this object so that it will no longer receive notifications.
- delete_observers()
Remove all observers associated with this object.
- notify_observers(*arg)
true.
How it works
First, we have to create a basic structure of the Notifier class which will act as an Observer. The update() method is the callback that the Observable module will use when notifying changes to the observer, and the method name needs to be update(). Let’s take an example of an application which keeps track of the bike mileage and reminds us of when we need to take the vehicle in for a scheduled bike service.require 'observer' class Bike include Observable attr_reader :mileage,:service def initialize(mileage =0, service = 3000) @mileage,@service = mileage, service add_observer(Notifier.new) end def log(miles) @mileage += miles notify_observers(self, miles) end endNext, write the update method in Notifier class
class Notifier def update(bike, miles) puts "The bike has logged #{miles} miles, totaling #{bike.mileage} miles traveled." puts "The bike needs to be taken in for a service!" if bike.service <= bike.mileage end end
By running the code we can see
bike = Bike.new(2300, 3000) bike.log(100) => "The bike has logged 100 miles, totaling 2400 miles traveled." bike.log(600) => "The bike has logged 300 miles, totaling 3000 miles traveled." => "The bike needs to be taken in for service!"
First, we create an instance of the Bike class with 2300 miles, and we set that it needs to be taken for service when it reaches 3000 miles.
I hope this example helps you understand ruby observer design pattern better.
Let me know in comments if you have any doubts or any implementation issues you have been facing recently. Thanks for reading!