Ruby中钩子方法的运用实例解析
通过使用钩子方法,可以让我们在Ruby的类或模块的生命周期中进行干预,可以极大的提高编程的灵活性。 类与模块相关
单件类相关
示例代码 module M1 def self.included(othermod) puts “M1 was included into #{othermod}” end end module M2 def self.prepended(othermod) puts “M2 was prepended to #{othermod}” end end class C include M1 include M2 end # 输出 M1 was included into C M2 was prepended to C module M def self.method_added(method) puts “New method: M##{method}” end def my_method; end end # 输出 New method: M#my_method 除了上面列出来的一些方法外,也可以通过重写父类的某个方法,进行一些过滤操作后,再通过调用super方法完成原函数的功能,从而实现类似钩子方法的功效,如出一辙,环绕别名也可以作为一种钩子方法的替代实现。 运用实例 写一个操作方法类似attr_accessor的attr_checked的类宏,该类宏用来对属性值做检验,使用方法如下: class Person include CheckedAttributes attr_checked :age do |v| v >= 18 end end me = Person.new me.age = 39 #ok me.age = 12 #抛出异常 实施计划: 使用eval方法编写一个名为add_checked_attribute的内核方法,为指定类添加经过简单校验的属性 def add_checked_attribute(klass,attribute) eval " class #{klass} def #{attribute}=(value) raise 'Invalid attribute' unless value @#{attribute} = value end def #{attribute}() @#{attribute} end end " end add_checked_attribute(String,:my_attr) t = "hello,kitty" t.my_attr = 100 puts t.my_attr t.my_attr = false puts t.my_attr 这一步使用eval方法,用class和def关键词分别打开类,且定义了指定的属性的get和set方法,其中的set方法会简单的判断值是否为空(nil 或 false),如果是则抛出Invalid attribute异常。 Setp 2 def add_checked_attribute(klass,attribute) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless value instance_variable_set("@#{attribute}",value) end define_method attribute do instance_variable_get "@#{attribute}" end end end 这一步更换掉了eval方法,同时也分别用class_eval和define_method方法替换了之前的class与def关键字,实例变量的设置和获取分别改用了instance_variable_set和instance_variable_get方法,使用上与第一步没有任何区别,只是一些内部实现的差异。 Step 3 def add_checked_attribute(klass,attribute,&validation) klass.class_eval do define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}",value) end define_method attribute do instance_variable_get "@#{attribute}" end end end add_checked_attribute(String,:my_attr){|v| v >= 180 } t = "hello,kitty" t.my_attr = 100 #Invaild attribute (RuntimeError) puts t.my_attr t.my_attr = 200 puts t.my_attr #200 没有什么奇特的,只是加了通过代码块验证,增加了校验的灵活性,不再仅仅局限于nil和false之间了。 Step 4 class Class def attr_checked(attribute,&validation) define_method "#{attribute}=" do |value| raise "Invaild attribute" unless validation.call(value) instance_variable_set("@#{attribute}",value) end define_method attribute do instance_variable_get "@#{attribute}" end end end String.add_checked(:my_attr){|v| v >= 180 } t = "hello,kitty" t.my_attr = 100 #Invaild attribute (RuntimeError) puts t.my_attr t.my_attr = 200 puts t.my_attr #200 这里我们把之前顶级作用域中方法名放到了Class中,由于所有对象都是Class的实例,所以这里定义的实例方法,也能被Ruby中的其它所有类访问到,同时在class定义中,self就是当前类,所以也就省去了调用类这个参数和class_eval方法,并且我们把方法的名字也改成了attr_checked。 Step 5 module CheckedAttributes def self.included(base) base.extend ClassMethods end end module ClassMethods def attr_checked(attribute,value) end define_method attribute do instance_variable_get "@#{attribute}" end end end class Person include CheckedAttributes attr_checked :age do |v| v >= 18 end end 最后一步通过钩子方法,在CheckedAttributes模块被引入后,对当前类通过被引入模块进行扩展, 从而使当前类支持引入后的方法调用,即这里的get与set方法组。 到此,我们已经得到了一个名为attr_checked,类似attr_accessor的类宏,通过它你可以对属性进行你想要的校验。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |