Observer Design Patter in Ruby

Observer Design Patter in Ruby

With this article, i want to show you guy about software design, and how to design it in ruby. Design patterns have always been a vital part of a quality software development. In fact, they let developers to work in a team effectively by setting up the common ground rules for a problem.

design-patterns.png

I. Observer Design

And in this post I’d like to introduce you to The Observer Pattern. Basically, observer is a behavioral type, or publish/subscribe pattern which allows a number of observer objects to see an event. So, if an observer object is subscribed to an event on the subject, on a change/update of that event on subject will publish/reflect the changes on the observing objects as well.

500px-Observer.svg.png

How To Use It

I will give you one problem and show the code why we need to use it. For example: An electric bulb’s state(on/off) will depend on the state of the switch it is directly connected to. So, if I turn the switch on then the bulb will be turned on, and vice-a-versa.

Now, think switch and bulb as objects. These objects will have two states: either on or off. However, the bulb’s state will entirely depend on the switch’s state.

Bored reading theoretical stuff?? Let’s see some code in action:

class Appliance
  attr_accessor :name, :state , :switch
  def initialize(name, switch = nil)
    @name = name
    @state = switch.nil? ? :off : switch.state
    @switch = switch
  end

  def update(changed_state)
    self.state = changed_state
  end

  def on?
    self.state == :on
  end

  def off?
    self.state == :off
  end
End

Code above shows an Appliance class, which I thought makes more sense. Since, we have bulb, fan, A.C. which are ultimately electrical appliances which have different attributes(of course) except one, which is the state(on/off).

One thing you’ll notice when you see your switch is that, it is integrated on a switch box, so here is our switch box:

class SwitchBox
  attr_accessor :name, :switches
  def initialize name
    @name = name
    @switches = []
  end
end

And before we have our switch, we would need the observer:

module Observer
  attr_reader :observers
  def initialize
    @observers = []
  end

  def add_observer observer
    @observers << observer
  end

  def delete_observer observer
    @observers.delete observer
  end

  def notify_observers method = :update
    self.observers.map do |observer|
      if observer.respond_to? method
        observer.send method, self.state
      else
        raise NoMethodError, "undefinded method #{method.to_s}"
      end
    end
  end
end

Finally we have our switch code as below:

class Switch
  include Observer

  attr_reader :name, :state
  STATES = { :off => :on, :on => :off }

  def initialize name, state = :off
    super()
    @name = name
    @state = state
  end

  def state=(new_state)
    return if self.state == new_state
    @state = new_state
    notify_observers
  end

  def on?
    self.state == :on
  end

  def off?
    self.state == :off
  end

  def change_state
    self.state = STATES[state]
  end
end

Yes, that is all we do, now we can run these code in our terminal

      switch_box = SwitchBox.new argf.gets.chomp
      switch1 = Switch.new argf.gets.chomp, :off

      switch_box.switches << switch1

      appliance = Appliance.new(argf.gets.chomp)
      appliance.switch = switch1
      switch1.add_observer(appliance)

      puts "Switch named '#{switch1.name}' is currently #{switch1.state.to_s.upcase}"
      puts "Appliance '#{appliance.name}' is currently #{appliance.state.to_s.upcase}"
      puts '-'*10
      puts 'Changing switch state!!'
      switch1.state = :on
      puts 'Switch state has been changed!!'
      puts '-'*10
      puts "Switch named '#{switch1.name}' is now #{switch1.state.to_s.upcase}"
      puts "Appliance '#{appliance.name}' is now #{appliance.state.to_s.upcase}

Conclusion

This article demonstrated how the Observer Pattern can be used with Ruby, and I hope the example presented here can give you the basic for implementing them on your own applications.

Document:


All Rights Reserved