|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
既然话题已经抄起,我打算今晚发篇博文再引导一下舆论方向,使它再火两天,抛砖引玉,而且赵劼先生一直在跟帖,使.NET阵营的我感到万分难得。在Ruby中,模块能够被其他模块包括(这里,其他模块也包含类,由于类也是模块)。经由过程包括模块,能够构建出庞大的类条理布局。本文注释了当包括一个模块时,Ruby外部都做了哪些实践的事情。
上面这段Ruby代码创立了A,B,C三个模块,C包括A和B。- A=Module.newB=Module.newmoduleCincludeA,B#Inclusionisdonebythislineend
复制代码 在模块C中,语句includeA,B完成了模块包括的事情,该语句以A,B两个模块为作为参数,挪用了模块C的include办法。统统都那末天然。
如今,把注重力放在include办法上,它的默许完成是Module#include。依据API文档,它以相反的按次对参数一一挪用append_features办法。对应的C函数是rb_mod_include。- staticVALUErb_mod_include(intargc,VALUE*argv,VALUEmodule){inti;IDid_append_features,id_included;CONST_ID(id_append_features,"append_features");CONST_ID(id_included,"included");for(i=0;i<argc;i++)Check_Type(argv[i],T_MODULE);while(argc--){rb_funcall(argv[argc],id_append_features,1,module);rb_funcall(argv[argc],id_included,1,module);}returnmodule;}
复制代码 从Ruby源码来看,除对参数模块挪用append_features办法外,还紧接着挪用了included办法。以是,文档形貌得其实不完全,大概还没有更新。
append_features和included都是回调办法。而append_features办法才是真正干活的中央,Ruby经由过程这个办法将包括的逻辑转移到了被包括的模块上。这类精致的计划能让你天真地自界说包括模块的举动,既能够在收回包括举措的模块中重界说include办法,也能够从泉源动手,重界说append_features办法。
再来看看append_features的默许完成Module#append_features都做了甚么。上面是API文档上给出的源码:- staticVALUErb_mod_append_features(VALUEmodule,VALUEinclude){switch(TYPE(include)){caseT_CLASS:caseT_MODULE:break;default:Check_Type(include,T_CLASS);break;}rb_include_module(include,module);returnmodule;}
复制代码 这个函数对照复杂,除范例平安反省,它只是rb_include_module函数的封装,我们进一步跟到rb_include_module函数中往看看。- voidrb_include_module(VALUEklass,VALUEmodule){intchanged=0;rb_frozen_class_p(klass);if(!OBJ_UNTRUSTED(klass)){rb_secure(4);}if(!RB_TYPE_P(module,T_MODULE)){Check_Type(module,T_MODULE);}OBJ_INFECT(klass,module);changed=include_modules_at(klass,RCLASS_ORIGIN(klass),module);if(changed<0)rb_raise(rb_eArgError,"cyclicincludedetected");if(changed)rb_clear_cache();}
复制代码 一样,rb_include_module函数也没有做几实践的事情,只是做了一些平安方面的反省,然后把次要事情交给了include_modules_at函数。- 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
复制代码- staticintinclude_modules_at(constVALUEklass,VALUEc,VALUEmodule){VALUEp;intchanged=0;constst_table*constklass_m_tbl=RCLASS_M_TBL(RCLASS_ORIGIN(klass));while(module){intsuperclass_seen=FALSE;if(RCLASS_ORIGIN(module)!=module)gotoskip;if(klass_m_tbl&&klass_m_tbl==RCLASS_M_TBL(module))return-1;/*ignoreifthemoduleincludedalreadyinsuperclasses*/for(p=RCLASS_SUPER(klass);p;p=RCLASS_SUPER(p)){switch(BUILTIN_TYPE(p)){caseT_ICLASS:if(RCLASS_M_TBL(p)==RCLASS_M_TBL(module)){if(!superclass_seen){c=p;/*moveinsertionpoint*/}gotoskip;}break;caseT_CLASS:superclass_seen=TRUE;break;}}c=RCLASS_SUPER(c)=rb_include_class_new(module,RCLASS_SUPER(c));if(FL_TEST(klass,RMODULE_IS_REFINEMENT)){VALUErefined_class=rb_refinement_module_get_refined_class(klass);st_foreach(RMODULE_M_TBL(module),add_refined_method_entry_i,(st_data_t)refined_class);FL_SET(c,RMODULE_INCLUDED_INTO_REFINEMENT);}if(RMODULE_M_TBL(module)&&RMODULE_M_TBL(module)->num_entries)changed=1;if(RMODULE_CONST_TBL(module)&&RMODULE_CONST_TBL(module)->num_entries)changed=1;skip:module=RCLASS_SUPER(module);}returnchanged;}
复制代码 include_modules_at函数做了很多事情,值得好好剖析一下。
起首,最外层的while轮回对module的先人链(ancestors)一一遍历。关于每一个module,内层的for轮回会反省module是不是已包括在klass的先人链中,假如已包括,则跳过,不然,把module拔出到c和c的父类之间。
在测试module是不是已包括在先人链的过程当中,变量superclass_seen用来判别是不是超出了一个非包括类,假如是,则不挪动拔出点。以是模块包括不会将模块插到先人链中下一个非包括类以后。
模块的拔出事情是由第31行代码完成的,它挪用rb_include_class_new函数为模块创立了一个包括类(includeclass),它以c的父类作为父类,接着又将这个包括类设置为c的父类。我们来看看rb_include_class_new函数是怎样创立包括类的。- 1234567891011121314151617181920212223242526272829
复制代码- VALUErb_include_class_new(VALUEmodule,VALUEsuper){VALUEklass=class_alloc(T_ICLASS,rb_cClass);if(BUILTIN_TYPE(module)==T_ICLASS){module=RBASIC(module)->klass;}if(!RCLASS_IV_TBL(module)){RCLASS_IV_TBL(module)=st_init_numtable();}if(!RCLASS_CONST_TBL(module)){RCLASS_CONST_TBL(module)=st_init_numtable();}RCLASS_IV_TBL(klass)=RCLASS_IV_TBL(module);RCLASS_CONST_TBL(klass)=RCLASS_CONST_TBL(module);RCLASS_M_TBL(klass)=RCLASS_M_TBL(RCLASS_ORIGIN(module));RCLASS_SUPER(klass)=super;if(RB_TYPE_P(module,T_ICLASS)){RBASIC(klass)->klass=RBASIC(module)->klass;}else{RBASIC(klass)->klass=module;}OBJ_INFECT(klass,module);OBJ_INFECT(klass,super);return(VALUE)klass;}
复制代码 第4行,class_alloc创立了一个新类,并为它设置了T_ICLASS标识,有了这个标识,Ruby就会以为它是一个包括类。接着就是一系列的初始化,将新类的外部表指向模块中对应的表。最初,将新类的klass指向模块,如许,包括类就创立完成了。
第17行有个RCLASS_ORIGIN宏,我们来看看它的感化是甚么,Ruby源码中,该宏的界说以下:- #defineRCLASS_ORIGIN(c)(RCLASS_EXT(c)->origin)
复制代码 能够看到,它指向一个类的origin成员。复杂来讲,它猎取某个类的原始类。原始类跟模块前置有着亲切的干系。关于模块前置的更多信息,请浏览《Ruby中模块前置的完成》这篇文章。
懂得这些信息以后,我们来总结一下。为了便于形貌,我们以文章开首的那段代码作为例子,往返顾一下模块包括的全历程。
也不知道,我同学昨天说数据挖掘很好。 |
|