Ruby 中的 module_function 和 extend self异同
在阅读开源的Ruby代码和编写可维护性的代码经常遇到这两者的使用,那么他们两者的共同点和区别是什么呢?
module_function
Ruby的module是method和constants的集合。module中的method又可分为instancemethod和modulemethod,当一个module被include进一个class,那么module中的method(注:没有被module_function标记的method)就是class中的instancemethod,instancemethod需要所在的class被实例化之后才能被调用;被module_function标记的method(不管该method是public或者private)就是modulemethod且instancemethod也会变成privatemethod,对于被include所在的class来说是privatemethod,object.module_name会出错。modulemethod都能被module_name.method_name调用,没有被module_function标记的publicmethod不能被module_name.method_name调用。
module中的module_function会把module中的method变成modulemethod且对于被include所在的class来说,modulemethod在module中是privatemethod故module_name.module_method能调用,而不能被object.module_name调用。
module中的publicmethod对于被include所在的class来说是instancemethod,故object.public_method_in_module能调用。如果想要非modulemethod能够被module调用(module_name.not_module_method),需要引入extendself(下文会讨论extendself)
#test.rb moduleMyModule defpublic_meth p"apublicmethod,ifthemoduleisincludedtoaclass,canbecallasobject.public_meth" end defmodule_method p"amodulemethod,canbecalledasmodule_name.module_method.butcannotbecallasobject.module_method" end private defprivate_method_to_module_function p"aprivate_method,butcanbecallasmodule_name.module_method,becauseitwasassignedtomodule_function" end defprivate_method p"Iamaprivatemethod" 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"publicmethodcannotbecalledbymodule_name.public_meth" end begin MyModule.private_method rescueNoMethodError p"privatemethodcannotbecalledbymodule_name.module_method" end classMyClass includeMyModule end obj=MyClass.new obj.public_meth begin obj.private_method rescueNoMethodError p"privatemethodinmodulecannotbecallbyobject.method_name" end begin obj.module_method rescueNoMethodError p"modulemethodcannotbecalledbyobject.method_name,forobject,modulemethodisprivateinstancemethod" end #调用 rubytest.rb "amodulemethod,canbecalledasmodule_name.module_method.butcannotbecallasobject.module_method" "aprivate_method,butcanbecallasmodule_name.module_method,becauseitwasassignedtomodule_function" "publicmethodcannotbecalledbymodule_name.public_meth" "privatemethodcannotbecalledbymodule_name.module_method" "apublicmethod,ifthemoduleisincludedtoaclass,canbecallasobject.public_meth" "privatemethodinmodulecannotbecallbyobject.method_name" "modulemethodcannotbecalledbyobject.method_name,forobject,modulemethodisprivateinstancemethod"
总结就是
•Themethodwillbecopiedtoclass'singletonclass
•Theinstancemethod'svisibilitywillbecomeprivate
extendself
Includeisforaddingmethodstoaninstanceofaclassandextendisforaddingclassmethods
extend本质是给class或者module添加classmethod
extendself让module中的instancemethod能够被module_name.instance_method调用,保留module中原本method的public或private属性,但又不像module_function一样把被标记的method变成private。
#!/usr/bin/envruby #encoding:utf-8 #test_extend.rb moduleMyModule extendself defpublic_meth p"apublic_methodextendedbyselfcanbecalledbymodule_name.public_methandobject.public_meth,includedbyaclass" private_method end private defprivate_method p"aprivatemethod,canbecallinmoduleinternal" end end classMyClass includeMyModule end MyModule.public_meth begin MyModule.private_method rescueNoMethodError p"privatemethodinextendselfmodulecannotbecalledmodule_name.private_method" end obj=MyClass.new obj.public_meth begin obj.private_method rescueNoMethodError p"privatemethodcannotbecalledbyobject.private_method" end #调用rubytest_extend.rb "apublic_methodextendedbyselfcanbecalledbymodule_name.public_methandobject.public_meth,includedbyaclass" "aprivatemethod,canbecallinmoduleinternal" "privatemethodinextendselfmodulecannotbecalledmodule_name.private_method" "apublic_methodextendedbyselfcanbecalledbymodule_name.public_methandobject.public_meth,includedbyaclass" "aprivatemethod,canbecallinmoduleinternal" "privatemethodcannotbecalledbyobject.private_method"
总结就是:
•Nomethodcopyinginvolved
•Nochangestomethodvisibility
总结
module_function改变module内原来method的public/private属性并把改method变成modulemethod,能够被module_name.module_method调用。
extendself就是在module自继承,不改变module中method的public/private属性,能够被module_name.public_method