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)

参考链接

0 条评论
您想说点什么吗?