module_function & extend sel

作者: 老码农不上班 | 来源:发表于2017-04-30 13:01 被阅读244次

    更多文章请访问独立博客 https://huangwenwei.com

    在阅读开源的 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_function
    Style/ModuleFunction: Use module_function instead of extend self. (https://github.com/bbatsov/ruby-style-guide#module-function)

    参考链接
    Self Improvement
    Is “extend self” the same as “module_function”?
    Include vs Extend in Ruby

    相关文章

      网友评论

      • auguszou:不建议这么用啊 看起来好丑 extend self
        老码农不上班:Style/ModuleFunction: Use module_function instead of extend self. (https://github.com/bbatsov/ruby-style-guide#module-function)

        rubocop 推荐使用 module_method

      本文标题:module_function & extend sel

      本文链接:https://www.haomeiwen.com/subject/omqxtxtx.html