`
jlaky
  • 浏览: 42882 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Design Patterns in Ruby [Digest 4] Observer

阅读更多

 

The Observer Pattern is useful when you need to inform other objects whenever one original change of object occurs. Such as SNS feeds feature, cache clear and so on...

 

Without this pattern the code will looks like below:


class Payroll
	def update( changed_employee )
		puts("Cut a new check for #{changed_employee.name}!")
		puts("His salary is now #{changed_employee.salary}!")
	end
end

class Employee
	attr_reader :name, :title
	attr_reader :salary

	def initialize( name, title, salary, payroll)
		@name = name
		@title = title
		@salary = salary
		@payroll = payroll
	end

	def salary=(new_salary)
		@salary = new_salary
		@payroll.update(self)
	end
end
 
payroll = Payroll.new
fred = Employee.new('Fred', 'Crane Operator', 30000, payroll)
fred.salary = 35000
 
The trouble of the above example is that we must hard code the relation of the two classes together and when we need to add some other informed objects, we must change the Employ class.
The changing of the Employ is driven by related objects. It is really ugly.

Now if we use observer pattern, the code will be:
class Employee
	attr_reader :name, :title
	attr_reader :salary

	def initialize( name, title, salary )
		@name = name
		@title = title
		@salary = salary
		@observers = []
	end

	def salary=(new_salary)
		@salary = new_salary
		notify_observers
	end

	def notify_observers
		@observers.each do |observer|
		observer.update(self)
		end
	end
	
	def add_observer(observer)
		@observers << observer
	end
	
	def delete_observer(observer)
		@observers.delete(observer)
	end
end
 
fred = Employee.new('Fred', 'Crane Operator', 30000.0)
payroll = Payroll.new
fred.add_observer( payroll )
fred.salary=35000.0
 
By building this general mechanism, we have removed the implicit coupling between the Employee class and the Payroll object. Employee no longer cares which or how many other objects are interested in knowing about salary changes; it just forwards the news to any object that said that it was interested.

The subject object in fact does most of the work, and the observer objects just tell the subject that when you ready, call me! The real observer for us in sense will be the one keeps track on the subject and the subject even does not know the observer is looking at him. 

Then refactoring the subject object Employee, and extract the observable support for every subject who need it:
class Subject
	def initialize
		@observers=[]
	end
	
	def add_observer(observer)
		@observers << observer
	end
	
	def delete_observer(observer)
		@observers.delete(observer)
	end
	
	def notify_observers
		@observers.each do |observer|
			observer.update(self)
		end
	end
end

 
class Employee < Subject
	attr_reader :name, :address
	attr_reader :salary
	
	def initialize( name, title, salary)
		super()
		@name = name
		@title = title
		@salary = salary
	end
	
	def salary=(new_salary)
		@salary = new_salary
		notify_observers
	end
end
 
The Ruby class only have one super class, so use the Module instead, then Employee will be:
class Employee
	include Subject
	attr_reader :name, :address
	attr_reader :salary
	
	def initialize( name, title, salary)
		super()
		@name = name
		@title = title
		@salary = salary
	end
	
	def salary=(new_salary)
		@salary = new_salary
		notify_observers
	end
end

 
The ruby use the Observable to implement Subject module.

The update(self) is pull method, the observer pull whatever it need from the subject.
GoF also give a push method looks like 
observer.update(self, :salary_changed, old_salary, new_salary)


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics