Observer and Singleton design patterns in Ruby
This is my first article in http://railsmagazine.com, it was published in issue 3, so basically I'm just republishing it here again.
Observer and singleton are two common design patterns that a programmer should be familiar with, however what made me write about them, is that both are there out of the box for you to use in ruby.
So let’s have a look at both and see how ruby help you use them directly:
Observer Design Pattern
According to Wikipedia:
The observer pattern (sometimes known as publish/subscribe) is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.
So how does ruby help you implementing this design pattern? Well, the answer is by mixing the observable module into your subject (observed object).
Let’s take an example, let’s suppose we have a banking mechanism that notifies the user by several ways upon withdrawal operations that leaves the account with balance less or equal to $0.5.
If we look deeply at this problem, we can qualify it as a good candidate for observer design pattern, where the bank account is our subject and the notification system as the observer.
Here is a code snippet for this problem and it’s solution:
# require the observer lib file
require “observer”
class Notifier
end
class EmailNotifier < Notifier
def update(bank_account)
if bank_account.balance <= 10
puts “Sending email to: ‘#{bank_account.owner}’ informing him with his account balance: #{bank_account.balance}$.”
# send the email mechanism
end
end
end
class SMSNotifier < Notifier
def update(bank_account)
if bank_account.balance <= 0.5
puts “Sending SMS to: ‘#{bank_account.owner}’ informing him with his account balance: #{bank_account.balance}$.”
# send sms mechanism
end
end
end
class BankAccount
# include the observable module
include Observable
attr_reader :owner,:balance
def initialize(owner,amount)
@owner,@balance = owner,amount
# adding list of observes to the account
add_observer EmailNotifier.new
add_observer SMSNotifier.new
end
# withdraw operation
def withdraw(amount)
# do whatever you need
@balance -=amount if (@balance - amount) > 0
# now here comes our logic
# issue that the account has changed
changed
# notify the observers
notify_observers self
end
end
account = BankAccount.new “Jim Oslen”, 100
account.withdraw 99.5
#=>Sending email to: ‘Jim Oslen’ informing him with his account balance: 0.5$.
#=>Sending SMS to: ‘Jim Oslen’ informing him with his account balance: 0.5$.
So to user ruby observer lib we have to implement four things:
1- Require the ‘observer’ lib and include it inside the subject (observed) class.
2- Declare the object to be ‘changed’ and then notify the observers when needed – just like we did in ‘withdraw’ method.
3- Declare all needed observers objects that will observe the subject.
4- Each observer must implement an ‘update’ method that will be called by the subject.
Observers in Rails
You can find observers in rails when using ActiveRecord, it’s a way to take out all ActiveRecord callbacks out of the model, for example a one would do this:
class User < ActiveRecord::Base
after_create :send_email
private
def send_email
#send a welcome email
end
end
A neater solution is to use Observers:
class UserObserver < ActiveRecord::Observer
def after_create(user)
#send a welcome email
end
end
You can generate the previous observer using the following command:
ruby script/generate observer User
You still can have observers that map to models that don’t match with the observer name using the ‘observe’ class method, you also can observe multiple models using the same method:
class NotificationObserver < ActiveRecord::Observer
observe :user, :post
def after_create(record)
#send thanks email
end
end
Finally don’t forget to add the following line inside config/environment.rb to define observers:
config.active_record.observers = :user_observer
Singleton Design Pattern
According to Wikipedia:
In software engineering, the singleton pattern is a design pattern that is used to restrict instantiation of a class to one object. (This concept is also sometimes generalized to restrict the instance to a specific number of objects – for example, we can restrict the number of instances to five objects.) This is useful when exactly one object is needed to coordinate actions across the system.
The singleton design pattern is used to have one instance of some class, typically there are many places where you might want to do so, just like having one database connection, one LDAP connection, one logger instance or even one configuration object for your application.
In ruby you can use the singleton module to have the job done for you, let’s take ‘application configuration’ as an example and check how we can use ruby to do the job:
# require singleton lib
require ‘singleton’
class AppConfig
# mixin the singleton module
include Singleton
# do the actual app configuration
def load_config(file)
# do your work here
puts “Application configuration file was loaded from file: #{file}”
end
end
conf1 = AppConfig.instance
conf1.load_config “/home/khelll/conf.yml”
#=>Application configuration file was loaded from file: /home/khelll/conf.yml
conf2 = AppConfig.instance
puts conf1 == conf2
#=>true
# notice the following 2 lines won’t work
# new method is private
AppConfig.new rescue(puts $!)
#=>private method `new’ called for AppConfig:Class
# dup won’t work
conf1.dup rescue(puts $!)
#=>can’t dup instance of singleton AppConfig
So what does ruby do when you include the singleton method inside your class?
1- It makes the ‘new’ method private and so you can’t use it.
2- It adds a class method called instance that instantiates only one instance of the class.
So to use ruby singleton module you need two things:
1- Require the lib ‘singleton’ then include it inside the desired
class.
2- Use the ‘instance’ method to get the instance you need.