Observer Design Patter in Ruby
Bài đăng này đã không được cập nhật trong 3 năm
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.
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.
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