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)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值