【Ruby】模块扩展方法介绍

2016-12-03 14:06:44来源:网络收集作者:Worker人点击

第七城市

当我们要扩展类的方法时,我们可以采取Mixin的方式将模块中的方法添加到类中,下面会对实现的几种方式进行详细介绍:


1. include和extend使用include,可以将模块中定义的方法作为实例方法添加到类中;使用extend,可以将模块中定义的方法作为类方法添加到类中。
下面的例子是include的使用方法。
module MyMod
def method
end
end
class MyClass
include MyMod
end
MyClass.new.method#正确
MyClass.method#错误下面的例子是extend的使用方法。
module MyMod
def method
end
end
class MyClass
extend MyMod
end
MyClass.new.method#错误
MyClass.method#正确
此外,Ruby针对模块还提供了一些Hook方法,这些方法会在模块被Mixin到类中时执行,其中self.included方法会在调用include时执行,self.extended方法会在调用extend时执行。具体示例如下:
module MyMod
def self.included(base)
puts "included into #{base}"
end
def self.extended(base)
puts "extended into #{base}"
end
end
class MyClass
include myMod#included into MyClass
extend myMod#extended into MyClass
end
2. 同时引入实例方法和类方法
使用include和extend可以很好地帮助我们分别引入模块中实例方法和类方法,但如果对于一个模块,我们需要把其中一部分的方法作为实例方法引入,而另一部分的方法作为类方法引入,使用这种处理方式就无法实现。对此,我们利用子模块来进行实现,示例如下:module MyMod
module InstanceMethods
def instance_method
end
end
module ClassMethods
def class_method
end
enddef self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
class MyClass
include MyMod
end

在该实现方法中,模块MyMod包括两个子模块InstanceMethods和ClassMethods,其中子模块InstanceMethods中的instance_method方法将作为实例方法引入,子模块ClassMethods中的class_method方法将作为类方法引入。在类MyClass中,通过include该模块,可以调用sef.included方法,进而分别引入子模块InstanceMethods和ClassMethods。


3. ActiveSupport::Concern相比于第二种方式,我们可以使用ActiveSupport::Concern来更好地进行处理(需要注意的是:使用前需要require "active_support")。下面的例子是ActiveSupport::Concern的使用方法,其中instance_method表示实例方法,class_method表示类方法。
module MyMod
extend ActiveSupport::Concern
def instance_method
end
module ClassMethods
def class_method
end
end
end该代码与第二种方式中代码的作用相同。但不同的是,在使用了ActiveSupport::Concern后,不仅可以省略子模块InstanceMethods,将instance_method方法直接放在模块中,还能省略receiver.extend和receiver.send,自动将类方法和实例方法加载到类中。
除了可以简化代码以外,ActiveSupport::Concern还有一个更大的作用是:当需要引入多个模块时,可以很好地解决模块相互之间的依赖关系。下面是rubydoc上关于ActiveSupport::Concern的讲解。
module Foo
def self.included(base)
base.class_eval do
def self.method_injected_by_foo
...
end
end
end
end
module Bar
def self.included(base)
base.method_injected_by_foo
end
end
class Host
include Foo # We need to include this dependency for Bar
include Bar # Bar is the module that Host really needs
end首先,在上面的代码中,类Host要引用模块Bar,但模块Bar需要调用模块Foo中定义的method_injected_by_foo方法,因此需要先引入模块Foo。但是,为什么不直接在模块Bar中直接引入模块Foo呢?这样可以在类Host中省去对模块Foo的引用,如下面代码所示:
module Foo
def self.included(base)
base.class_eval do
def self.method_injected_by_foo
...
end
end
end
end
module Bar
include Foo
def self.included(base)
base.method_injected_by_foo
end
end
class Host
include Bar
end但该代码并不能起作用,这是因为当模块Foo被引入时,其base为模块Bar,而不是类Host,这样我们就无法在类Host中调用模块Foo中的method_injected_by_foo方法。此时,使用ActiveSupport::Concern可以很好地解决此模块依赖的问题,代码如下:
module Foo
extend ActiveSupport::Concern
included do
class_eval do
def self.method_injected_by_foo
...
end
end
end
end
module Bar
extend ActiveSupport::Concern
include Foo
included do
self.method_injected_by_foo
end
end
class Host
include Bar # works, Bar takes care now of its dependencies
end转载请注明出处:/2014th7cj/d/file/p/20161203/lkk1aqzfzbz

版权声明:本文为博主原创文章,未经博主允许不得转载。

第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台