以前看用java实现设计模式,很是头疼,那么多的java概念融合在里面,完全影响了对模式自身的理解。实现起来也是相当麻烦。但是在ruby中,模式理解起来是那么的容易,倒是觉得使用ruby来理解设计模式挺靠谱的。
先介绍一种模式-观者者模式。
百度百科名片 写道
观察者模式(有时又被称为发布-订阅模式、模型-视图模式、源-收听者
通俗点说就是A对象(被观察)通知另一个(一些)对象(观察者)自己发生改变了,改变了什么,至于你们这些对象要做什么就不关我的事了,你们自己做去吧!耦合度就此降低了。。。
一段简单的Ruby 观察者模式的实现:
#!/usr/bin/env ruby require 'observer' class CheckWaterTemperature include Observable def run loop do changed() notify_observers(Time.now) sleep 1 end end end class ClockView def update (time) puts time end end checker = CheckWaterTemperature.new checker.add_observer(ClockView.new) checker.run
这段中代码包含两个类,顾名思义ClockView是用来展示的,而Clock就是被观察的对象了。当被观察的对象有改变的时候,应该通知观察者们它已经改变了。
在Ruby中通过mix-in方式(类似于java的接口)实现被观察的对象,它的接口有这些:
#add_observer //增加观察者 #changed //改变当前的状态 #changed? // 查询放钱的状态 #count_observers //计数 #delete_observer //删除一个 #delete_observers //删除一些 #notify_observers //通知观察者们,我已经改变了
而观察者只要实现一个方法update,这样通知发生的时候,观察者要做什么就由update实现来决定了。。很容易理解啊。。
上面的示例是通过控制台实现的,增加一个GUI实现,TK实现的,文档可真不好找啊..
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'observer' require 'thread' require 'tk' =begin ruby/tk简单的时钟 =end class Clock #观察者模式 include Observable def getPointAngle(time) #获取以y轴为线顺时针的角度,例如:3点钟则时针的角度为90度 sec_angle = time.sec / 60.0 * 360 min_angle = time.min / 60.0 * 360 + sec_angle / 360 / 60 hour_angle = time.hour.divmod(12)[1] / 12.0 * 360 + min_angle / 360 * 30 #转换成以xy轴的角度,例如3点钟,则时针的角度为0度,12点时针的角度为180度 return [hour_angle, min_angle, sec_angle].collect do |x| x <= 90 ? 90 -x : 450 - x end end def run() #一秒种刷新一次界面 loop do angles = self.getPointAngle(Time.now) changed() notify_observers(angles) sleep 1 end end end class ClockView LENGTH_ARRAY = [40, 50, 70] def initialize(root) @cur_sec_line = nil @cur_hour_line = nil @cur_min_line = nil @canvas = TkCanvas.new(root) @canvas.pack('side' => 'left', 'fill' => 'both') teitem = TkcText.new(@canvas, "text" => "12", "coords" => [95,10]) teitem = TkcText.new(@canvas, "text" => "3", "coords" => [195,95]) teitem = TkcText.new(@canvas, "text" => "6", "coords" => [95,195]) teitem = TkcText.new(@canvas, "text" => "9", "coords" => [5,95]) end def update(angles) coords = Array.new #将角度转换成在界面上的坐标 angles.to_a().each_with_index do |mangle, index| cy = Math.sin(mangle / 180 * Math::PI) * LENGTH_ARRAY[index] cx = Math.cos(mangle / 180 * Math::PI) * LENGTH_ARRAY[index] cx = cx + 100 cy = 100 - cy coords[index] = [cx, cy] end @cur_sec_line != nil and @cur_sec_line.delete() @cur_min_line != nil and @cur_min_line.delete() @cur_hour_line != nil and @cur_hour_line.delete() hline = TkcLine.new(@canvas, 100, 100, coords[0][0], coords[0][1], "width" => "3") mline = TkcLine.new(@canvas, 100, 100, coords[1][0], coords[1][1], "width" => "2") sline = TkcLine.new(@canvas, 100, 100, coords[2][0], coords[2][1], "width" => "1") [hline, mline, sline].map { |aline| aline.fill 'black' } @cur_sec_line = sline @cur_hour_line = hline @cur_min_line = mline end end root = TkRoot.new do title '怀旧时钟' geometry "200x200+1000+80" end clock = Clock.new() clock_view = ClockView.new(root) clock.add_observer(clock_view) Thread.new { clock.run } Tk.mainloop