關(guān)于工作和讀書的筆記
[原創(chuàng)文章歡迎轉(zhuǎn)載,但請(qǐng)保留作者信息]Justin 于 2010-01-24
前面已經(jīng)學(xué)過,用虛函數(shù)來實(shí)現(xiàn)接口是再自然不過的事情。這里要說的是,除了直接單純使用虛函數(shù)機(jī)制,還有一些別的方法。
大師說的第一種,是模板模式,利用非虛擬接口函數(shù)(Non-Virtual Interface, NVI)來實(shí)現(xiàn)。此模板(設(shè)計(jì)模式中的模板方法,template method)非彼模板(C++中的模板,template),而且也沒那么難:
在父類中提供一個(gè)公有函數(shù)(接口),然后在其中調(diào)用真正干活的私有虛函數(shù)(默認(rèn)實(shí)現(xiàn));子類對(duì)象通過公有繼承自然得到了這個(gè)函數(shù)(接口),如果子類沒有編寫自己的實(shí)現(xiàn)版本,最終執(zhí)行的就是默認(rèn)的實(shí)現(xiàn);如果子類實(shí)現(xiàn)了自己的版本,調(diào)用的就是子類的版本。(是不是和上節(jié)課的1.5有點(diǎn)親戚關(guān)系?) class ?AClass {??? public :?????? void ? interface ()?????? {?????????printf( " in?base::interface()\n " );?????????do_interface();??????} ??? private :?????? // the?default?implementation ?????? virtual ? void ?do_interface()?????? {?????????printf( " in?AClass::do_interface()\n " );??????} } ; class ?AClassDerived?:? public ?AClass {??? private : /**/ /* ?derived?can?has?its?own?implementation,?but?not?a?must..??????virtual?void?do_interface()??????{?????????printf("in?AClassDerived::do_interface()\n");??????} */ } ; 【給自己的】如果看到這里還會(huì)想:為什么是私有的虛函數(shù)?就搜“與大蝦對(duì)話:領(lǐng)悟設(shè)計(jì)模式”復(fù)習(xí)復(fù)習(xí)……
在父類中提供一個(gè)公有函數(shù)(接口),然后在其中調(diào)用真正干活的私有虛函數(shù)(默認(rèn)實(shí)現(xiàn));子類對(duì)象通過公有繼承自然得到了這個(gè)函數(shù)(接口),如果子類沒有編寫自己的實(shí)現(xiàn)版本,最終執(zhí)行的就是默認(rèn)的實(shí)現(xiàn);如果子類實(shí)現(xiàn)了自己的版本,調(diào)用的就是子類的版本。(是不是和上節(jié)課的1.5有點(diǎn)親戚關(guān)系?)
看了第一種方法的大多數(shù)同學(xué)都會(huì)有這種想法:這不還是要用虛函數(shù)么?穿個(gè)wrapper的馬甲就認(rèn)不出你了?大師馬上說第二個(gè)替代產(chǎn)品是策略模板(strategy pattern),利用函數(shù)指針實(shí)現(xiàn):
這種方法的實(shí)質(zhì),就是把接口函數(shù)的實(shí)現(xiàn)拿到了類之外。類之中只聲明接口的形式,只定義一個(gè)函數(shù)指針。真正干活的函數(shù)(實(shí)現(xiàn))都不是類的成員。這樣做帶來了一定的靈活性,具體采用哪種實(shí)現(xiàn)與類的繼承關(guān)系是獨(dú)立無關(guān)聯(lián)的;同時(shí),非類成員函數(shù)也有局限性:無法訪問類的非公有成員。如果把函數(shù)定義為友元或利用公有函數(shù)輸出私有成員,又會(huì)破壞原設(shè)計(jì)的 封裝。如下代碼所示: class ?AClass {??? public :??????typedef? void ? * (Interface)( /**/ /* param.. */ );?????? explicit ?AClass(?Interface?pint? = ?defaultInterface)?:?pInterface(pint)?????? {} ?????? // .. ??? private :??????Interface?pInterface; // .. } ; 在構(gòu)造AClass對(duì)象的時(shí)候即可指定Interface的真身,雖然,它無法直接訪問AClass的非公有成員。
這種方法的實(shí)質(zhì),就是把接口函數(shù)的實(shí)現(xiàn)拿到了類之外。類之中只聲明接口的形式,只定義一個(gè)函數(shù)指針。真正干活的函數(shù)(實(shí)現(xiàn))都不是類的成員。這樣做帶來了一定的靈活性,具體采用哪種實(shí)現(xiàn)與類的繼承關(guān)系是獨(dú)立無關(guān)聯(lián)的;同時(shí),非類成員函數(shù)也有局限性:無法訪問類的非公有成員。如果把函數(shù)定義為友元或利用公有函數(shù)輸出私有成員,又會(huì)破壞原設(shè)計(jì)的 封裝。如下代碼所示:
在構(gòu)造AClass對(duì)象的時(shí)候即可指定Interface的真身,雖然,它無法直接訪問AClass的非公有成員。
估計(jì)大師也覺得指針在C++里簡(jiǎn)單一些,于是更推崇用C++的庫(如TR1中的function)來管理接口函數(shù)。
原理和函數(shù)指針是一樣的,只不過因?yàn)橛昧藢?duì)象來管理資源,使得應(yīng)用更加靈活。當(dāng)然,要付出更多一點(diǎn)的代碼體積和運(yùn)行時(shí)間代價(jià)。 class ?AClass { // ?all?are?the?same?with?the?funtion?pointer?version // ?except?for: ???typedef?std::tr1::function void ?( /**/ /* param.. */ ) > ?Interface; // .. } ;
原理和函數(shù)指針是一樣的,只不過因?yàn)橛昧藢?duì)象來管理資源,使得應(yīng)用更加靈活。當(dāng)然,要付出更多一點(diǎn)的代碼體積和運(yùn)行時(shí)間代價(jià)。
大師在最后才說出了最經(jīng)典的策略模式實(shí)現(xiàn),也是我覺得比較漂亮且容易理解的實(shí)現(xiàn)方式。
用兩個(gè)類搞定: class ?AInterface{??? public :?????? // ..? ?????? virtual ? void ?DoInterface( /* ?param..? */ );??? // ..? };AInterface?defaultInterface; class ?AClass{??? public :?????? explicit ?AClass(AInterface? * ?pinter? = ? & defaultInterface)?:?pInter(pinter)??????{}??????? void ?TryInterface()??????{?????????pInter -> DoInterface();??????}??? // ..? ??? private :??????pInterface? * ?pInter;??? // ..? };
用兩個(gè)類搞定:
看到最后,似乎本課的思想就是用模式設(shè)計(jì)(template pattern或strategy pattern)來代替簡(jiǎn)單的虛函數(shù)設(shè)計(jì)。但是,大師臨走前又說了:以上只是舉例,為的是說明其實(shí)除了簡(jiǎn)單的虛函數(shù)外,還有很多種可能的方式來替代它完成設(shè)計(jì)需要。
Copyright @ Justin.H Powered by: .Text and ASP.NET Theme by: .NET Monster