關(guān)于工作和讀書的筆記
[原創(chuàng)文章歡迎轉(zhuǎn)載,但請保留作者信息]Justin 于 2009-12-20
大師說了,C++的設(shè)計(jì)還是有缺陷的:它無法把接口(interface)的設(shè)計(jì)和實(shí)現(xiàn)(implementation)的設(shè)計(jì)完全劃分開來。比如說在一個(gè)類的(接口)聲明當(dāng)中,總是或多或少的會泄漏一些實(shí)現(xiàn)上的細(xì)節(jié),雖然這樣做與接口的設(shè)計(jì)并沒有太多聯(lián)系。有同學(xué)說應(yīng)該多放些代碼一起炒冷飯,是個(gè)好主意,下面是書中的修改版本,大致是一樣的。
這些實(shí)現(xiàn)上的細(xì)節(jié)往往需要引用其他頭文件中相關(guān)對象的定義(比如說下面的代碼),從而產(chǎn)生了對這些頭文件的(在編譯時(shí)的)依賴。因此每次這些文件中的某個(gè)有變化時(shí),依賴它的所有文件都需要重新編譯。
【注意】這里貌似邏輯不是很順:就算沒有那些私有成員的聲明,接口函數(shù)的返回值如果是string或是BClass等類型,不還是一樣需要依賴引用其他頭文件嗎?其實(shí)這是兩種不一樣的情況,實(shí)現(xiàn)和接口。前面說的實(shí)現(xiàn)細(xì)節(jié)的泄漏是會導(dǎo)致編譯依賴的,因?yàn)榫幾g器需要了解這些類型對象的大小進(jìn)而為其分配內(nèi)存空間;但是接口,比如說函數(shù)的返回值或是參數(shù)表中的參數(shù),就不需要編譯器去考慮分配內(nèi)存的問題,因此也就沒有所謂的編譯依賴了。問題知道了,那么解決辦法呢,大師提出“骨肉分離法”(嗯……其實(shí)是我的杜撰@#¥%):將聲明(declaration)和定義(definition)分開。
呃……下面的比喻,最好吃完飯?jiān)倮^續(xù)。如果說接口是一個(gè)類的骨架,那么實(shí)現(xiàn)就是他的血肉;如果說聲明讓你摸到了骨頭,那么定義應(yīng)該就是血和肉生長的地方。根據(jù)骨肉分離法,對于一個(gè)AClass類,第一步先把血肉(定義/實(shí)現(xiàn))剝離開,只留下骨架。然后找個(gè)盒子(新建一個(gè)類,比如說AClassImpl),把血肉放進(jìn)去。接下來還有一步,在骨頭盒子里(原AClass類)加一條繩子連著血肉盒子(一個(gè)指向AClassImpl的指針),這樣才不至于讓骨肉真正的分離,只要找到了骨頭盒子,就一定能找到血肉盒子,然后對于這個(gè)“可憐”的AClass來說,它的全部“零件”都是完整的,啥也沒丟,但是做到了骨肉分離。
也做到了沒有編譯依賴。因?yàn)閷τ贏Class的用戶來說,他們面對的將是一個(gè)沒有定義的類,這個(gè)類的后繼改動(dòng),只要不涉及接口的改動(dòng),都不會導(dǎo)致用戶程序的重新編譯。看到這里想想工作時(shí)看到的代碼,原來前輩也有看過啊……對比前面的例程,給一個(gè)“骨肉分離”了的版本吧:
前面的文字是自己的理解,而大師的真言是這樣的:
如果覺得骨肉分離太殘忍,大師還有另外一個(gè)工具:工廠(factory)。第二種方法中,抽象類/接口類提供了所有接口的純虛函數(shù)形式:會有該類的子類去實(shí)現(xiàn)這些接口。與此同時(shí),在抽象類/接口類中還會有一個(gè)靜態(tài)(static)的工廠函數(shù)(比如create()/produce()/factory()……),這個(gè)函數(shù)實(shí)際上起到了構(gòu)造函數(shù)的作用,它“制造”出子類對象來完成真正的任務(wù),同時(shí)返回這個(gè)對象的指針(通常是智能指針如shared_ptr)。憑借這個(gè)返回的指針就可以進(jìn)行正常的操作,同時(shí)不會有編譯依賴的擔(dān)心。一個(gè)簡陋的代碼見下:
Copyright @ Justin.H Powered by: .Text and ASP.NET Theme by: .NET Monster