module_function & extend self & inheritances & extend & include in Ruby
在阅读开源的 Ruby 代码和编写可维护性的代码经常遇到这两者的使用,那么他们两者的共同点和区别是什么呢?
module_function
Ruby 的模块 (module) 是方法和常量的集合。模块中的方法又可分为实例方法和模块方法, 当一个模块被 include 到一个 Ruby 类时 ,模块中的方法(注:没有被 module_function 关键字标记的方法)就是类中的实例方法,需要所在的类被实例化之后才能被调用;被 module_function 关键字标记的方法称为模块方法 (module method),被标记之后模块中实例方法也会变成私有方法。调用模块方法的方式为:module_name.method_name
,而在模块中没有被 module_function 关键字标记的方法不能这样被调用。
模块中的 module_function 关键字会把对应的方法编程模块方法且对于被 include 引入到所在的类来说不可见,所以不能被实例化对象调用。
如果要让模块方法也能以 module_name.method_name
的方式调用,可以考虑 extend self
# test.rb
module MyModule
def public_meth
p "a public method, if the module is included to a class , can be call as object.public_meth"
end
def module_method
p "a module method,can be called as module_name.module_method. but can not be call as object.module_method"
end
private
def private_method_to_module_function
p "a private_method, but can be call as module_name.module_method, because it was assigned to module_function"
end
def private_method
p "I am a private method"
end
module_function :module_method, :private_method_to_module_function
end
MyModule.module_method
MyModule.private_method_to_module_function
begin
MyModule.public_meth
rescue
p "public method can not be called by module_name.public_meth"
end
begin
MyModule.private_method
rescue NoMethodError
p "private method can not be called by module_name.module_method"
end
class MyClass
include MyModule
end
obj = MyClass.new
obj.public_meth
begin
obj.private_method
rescue NoMethodError
p "private method in module can not be call by object.method_name"
end
begin
obj.module_method
rescue NoMethodError
p "module method can not be called by object.method_name, for object, module method is private instance method"
end
#调用
ruby test.rb
"a module method,can be called as module_name.module_method. but can not be call as object.module_method"
"a private_method, but can be call as module_name.module_method, because it was assigned to module_function"
"public method can not be called by module_name.public_meth"
"private method can not be called by module_name.module_method"
"a public method, if the module is included to a class , can be call as object.public_meth"
"private method in module can not be call by object.method_name"
"module method can not be called by object.method_name, for object, module method is private instance method"
总结就是
- The method will be copied to class' singleton class
- The instance method's visibility will become private
extend self
Include is for adding methods to an instance of a class and extend is for adding class methods
extend 本质是给类或者模块添加类方法 。
extend self 让模块中的实例方法能够被 module_name.method_name
调用,同时保留模块中原方法的 public/private 属性,但又不像 module_function 一样把被标记的方法变成私有方法。
#!/usr/bin/env ruby
# encoding: utf-8
# test_extend.rb
module MyModule
extend self
def public_meth
p "a public_method extended by self can be called by module_name.public_meth and object.public_meth, included by a class"
private_method
end
private
def private_method
p "a private method, can be call in module internal"
end
end
class MyClass
include MyModule
end
MyModule.public_meth
begin
MyModule.private_method
rescue NoMethodError
p "private method in extend self module can not be called module_name.private_method"
end
obj = MyClass.new
obj.public_meth
begin
obj.private_method
rescue NoMethodError
p "private method can not be called by object.private_method"
end
# 调用 ruby test_extend.rb
"a public_method extended by self can be called by module_name.public_meth and object.public_meth, included by a class"
"a private method, can be call in module internal"
"private method in extend self module can not be called module_name.private_method"
"a public_method extended by self can be called by module_name.public_meth and object.public_meth, included by a class"
"a private method, can be call in module internal"
"private method can not be called by object.private_method"
总结就是:
- No method copying involved
- No changes to method visibility
inheritances & extend & include 的区别
- inheritance :子类继承了父类的属性,包括访问这些属性的权限 (public/protected/private),与其他编程语言不同的是,Ruby 能够访问父类的私有方法,甚至还可以把父类的私有方法转化为公有方法。
- include :在指定的类中 mixed in 模块 (module) 中定义的方法作为该类的实例方法。
include model
等同于把模块中的代码复制到所在类中,减少代码重复。通过这种方式,突破了 Ruby 只允许单继承的限制。 - extend :把模块中的方法引入到当前类,并作为类方法。
总结
module_function
改变模块内原方法的 public/private 属性并设置成模块方法,能够通过 module_name.method_name
的方法调用。
extend self
就是在模块内自继承,不改变模块中方法的 public/private 属性,同样也能通过 module_name.public_method
的方法调用。
另外,rubocop 推荐使用 module_functionStyle/ModuleFunction: Use module_function instead of extend self. (https://github.com/bbatsov/ruby-style-guide#module-function)