注册会员 登录
Project1 返回首页

zhangbanxian的个人空间 https://rpg.blue/?79298 [收藏] [复制] [分享] [RSS]

日志

半仙的ruby研究之旅(三)ruby的回调方法

已有 483 次阅读2013-1-2 21:54 |个人分类:ruby研究

   说起回调(callback)这个概念呢,应该就得提提c语言的回调函数了,在windows系统中,我们是如何创建一个窗口的?
  众:用CreateWindow这个api啊。
  回答得很好,那么在这之前我们首先得用RegisterClass注册一个类,就像我们需要一个实例必须先创建类一样,这里就用到回调函数了:
  WNDCLASS wc;wc.lpfnWndProc = &WndProc;wc.lpszClassName = szWindowClass;...;
  RegisterClass(&wc);
  CreateWindow(szWindowClass,...);
  这就是c语言创建一个窗口的基本流程,而这个WndProc就是所谓的回调函数了,你说为啥要用回调函数?一个窗口往往就代表着一个程序个体,在一个流程里判断窗口的状态,并操作各个窗口,不觉得很怪异么?其实,窗口的处理都是大同小异,所以我们只需要分别定义各种窗口对获取的消息所有的不同反应即可,把何时执行WndProc的权力交给系统,系统觉得应该执行WndProc了就执行WndProc,然后就按照你设定的条件分歧执行相应的语句。
  这种由系统调用的函数,就被称作回调函数。与之类似的还有EnumWindows、SetTimer等也要用到回调函数。
  同样的,ruby里也有许多的回调方法:
    Object#method_missing           调用未定义的方法时自动调用
    Module#const_missing            调用未定义的常量时自动调用
    Object#singleton_method_added   定义特殊方法(包括重定义)时自动调用
    Object#singleton_method_removed 特殊方法被取消/删除时自动调用
          /singleton_undefined
    Module#method_added             类或模块定义方法(包括重定义)时自动调用
    Module#method_removed           取消/删除方法时自动调用
          /method_undefined
    Module#included/                模块被include时自动调用
    Module#extended                 模块被extend时自动调用
    Class#inherited                 类被继承时自动调用
   只要满足条件并且该方法存在时,系统就会自动调用这些回调方法。按惯例举个例子:
   class Fucker
    def self.inherited(child)
      print "#{child} is #{self}'s child"
    end
   end
   class Fucker_Child < Fucker;end  #=>Fucker_Child is Fucker's child
   关于这些方法的具体参数,去查一下ruby参考手册就很容易找到的,这里就不详细介绍了。
   嗯,相信各位已经了解这些回调方法的用法了...嗯,原来就这么几个吗?也太容易了吧,其实远不止这些,比如Array#sort就调用到了object.<=>,如果我们重定义<=>,事情就会变得很有趣...还觉得不够味?嘿,眼界别太狭窄,难道只有别人写的才能叫系统吗?你自己写的rm战斗系统就不叫系统了?其实回调换种说法就是被调方回过头调用调用方(虽然rm中调用方和被调方的界限不是很清晰- -b),即在写底层的时候既考虑到未来的需求,又不破坏整体封装性的一种写法,再说得现实一点就是你如果要发布一个脚本,又不想让对方修改你的脚本,你就可以考虑用这种思想提供一个回调接口以满足使用者的需求,不然你的脚本说不定就会出现各种所谓的改进版本xd...我们还是用例子来说话吧:
  def each_events
     $game_map.events.each_value{|i| i.do_something}
  end
 #事件脚本1
 class ::Game_Event
   def do_something
     move_down
   end
 end
 each_events #=>所有事件向下移动
 #事件脚本2
 class ::Game_Event
   def do_something
     move_up 
   end
 end
 each_events #=>所有事件向上移动
   假设我们的事件列表固定为$game_map.events,就可以这样提供一个空方法接口以供使用者定义...什么?你说这不是函数指针?很显然,这样写更符合面向对象的写法。但你要把方法当参数传进去也是完全可以的,我们可以用method方法将方法实例化...但ruby还有一个更为优雅的作法就是利用块,看看用块实现同一功能的办法吧...
 def each_events
   $game_map.events.each_value{|i| yield(i)}
 end
 #事件脚本1
 each_events{|i|i.move_down} #=>所有事件向下移动
 #事件脚本2
 each_events{|i|i.move_up} #=>所有事件向上移动
   这个yield就是用于调用传送进来的块的,yield的参数则回传给块的参数,就像回调函数那样。哦,原来Hash#each_value就是这样实现的啊?嗯,不错,不过是c层面上的实现。那为什么说块要优于实例化Method呢?其一,块的传递是作为一个附加参数传入的,有别于一般参数,用yield调用时也是一对一,很清晰明了,毕竟像遍历数组这样的事,定义函数名就显得很繁琐;其二,就得说说context的事了,所谓context就是当前函数的堆栈...块会保有当前的context(其实,块就相当于一个保留context的匿名方法,只不过语法不同,以及将其实例化的方法是proc而不是method),如此便可以实现双重回调...
 def is_any_event_dir?(dir)
   each_events do |i|
     return true if i.direction == dir
   end
   false
 end
   不过,说真的,对于ruby这种可以任意重定义方法的语言而言,回调这个概念已经不再重要了,只要我们需要随时都可以推倒之前封装好的类和模块,剩下的就是一些语言习惯和爱好之类的问题了...ruby挖空心思实现了各种各样写法的目的也就在于此吧,让我们把写程序当成一个游戏,嘿,这就是ruby存在的意义吧...


鸡蛋

鲜花

评论 (0 个评论)

facelist doodle 涂鸦笔

您需要登录后才可以评论 登录 | 注册会员

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-2 22:27

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

返回顶部