WINDOWS與設(shè)計(jì)模式
作為C++的堅(jiān)實(shí)粉絲,我一直很排斥JAVA,并不是JAVA這種語(yǔ)言不好,而是Java迷的那副嘴臉,事事都要與C++爭(zhēng),并且還堅(jiān)稱JAVA比C++,甚至連執(zhí)行效率都要?jiǎng)龠^(guò)C++,什么JIT運(yùn)行時(shí)能監(jiān)視代碼,選取執(zhí)行頻率最高的代碼,根據(jù)特定的平臺(tái),進(jìn)行特別處理,生產(chǎn)出最優(yōu)化的機(jī)器代碼;又說(shuō)什么垃圾回收能夠解決C++中的內(nèi)存管理問(wèn)題,并且內(nèi)存分配遠(yuǎn)遠(yuǎn)勝過(guò)C++中的手工人肉管理內(nèi)存的毛病,其實(shí),內(nèi)存管理從來(lái)就不是C++中的嚴(yán)重問(wèn)題,只要設(shè)計(jì)得好,應(yīng)用層中的代碼甚少可以不出現(xiàn)new, delete。至少delete可以不出現(xiàn),大家知道WHY的;種種論調(diào),無(wú)理取鬧之極。而最令我受不了的,就是他們閉口開(kāi)口,都離不開(kāi)設(shè)計(jì)模式,搞得設(shè)計(jì)模式好像成為了JAVA的專有產(chǎn)品。須知,第一本設(shè)計(jì)模式的書(shū),也是最最經(jīng)典的那本四人書(shū),其對(duì)設(shè)計(jì)模式的實(shí)現(xiàn)語(yǔ)言,采用的就是SmallTalk 和C++,一開(kāi)始就不關(guān)JAVA的一點(diǎn)事情,并且書(shū)中C++還占了較大的比重。關(guān)于四人書(shū),最讓我欣慰的一件事情就是,四位巨頭并沒(méi)有響應(yīng)JAVA迷的強(qiáng)烈呼聲,采用JAVA來(lái)實(shí)現(xiàn)設(shè)計(jì)模式的第二版。當(dāng)然,我也承認(rèn),用JAVA來(lái)實(shí)現(xiàn)設(shè)計(jì)模式,確實(shí)來(lái)得要比C++清爽,JAVA的這種語(yǔ)言,好像就是專為設(shè)計(jì)模式量身訂做。只可惜,市面上任何一本JAVA的設(shè)計(jì)模式書(shū),沒(méi)有一本及得上我們C++的那一本設(shè)計(jì)模式圣經(jīng),C++中不必再需要設(shè)計(jì)模式的書(shū)了,因?yàn)樽詈玫臅?shū)就已經(jīng)擺在那里了,涵蓋了設(shè)計(jì)模式中的方方面面,濃縮精華得很。突然想起,C++的教材也不需要那么多,因?yàn)槔蠣斪右呀?jīng)寫(xiě)了一本最好的書(shū)了,其他書(shū)的內(nèi)容,都已經(jīng)涵蓋在那本C++語(yǔ)言圣經(jīng)中了。至于那些不被C++圣經(jīng)所提及的,那都是一些走火入魔的玩意,玩玩還可以,開(kāi)闊開(kāi)闊視野也不錯(cuò),但真要用在實(shí)際項(xiàng)目中,還是少用為妙。罪過(guò)罪過(guò),C++中的好書(shū)確實(shí)有好幾本,不宜一棍子打死。有趣的是,設(shè)計(jì)模式在JAVA中被捧上了天,沒(méi)有設(shè)計(jì)模式,JAVA就沒(méi)法活下去。反而C++作為設(shè)計(jì)模式的第一實(shí)現(xiàn)語(yǔ)言,卻不怎么感冒。不能說(shuō)被輕視,但起碼沒(méi)有普遍被重視。即使C++中的高手,也沒(méi)有對(duì)設(shè)計(jì)模式如何如何的推崇備至,他們貌似更加喜歡玩語(yǔ)法糖,熱衷于在C++中模擬出其他語(yǔ)言的特性。這也難怪,C++有四種編程典范,設(shè)計(jì)模式的主要應(yīng)用就在于面向?qū)ο螅嫦驅(qū)ο蟛贿^(guò)是其中最不成熟的一種,在C++中,最成熟的要數(shù)基于對(duì)象的編程,當(dāng)然,拜C語(yǔ)言成熟,面向過(guò)程也熟得要爛了,泛型這東西也挺前衛(wèi)的,而C++中的面向?qū)ο螅恢背裘阎瑹o(wú)論怎么努力,都難以搬上臺(tái)面。注意,C++中的面向?qū)ο笈c虛函數(shù)是兩碼事,至少在我看來(lái)是這樣的。
好了,該談?wù)勎覍?duì)設(shè)計(jì)模式的看法了,一句話,我對(duì)設(shè)計(jì)模式殊無(wú)好感,源于我內(nèi)心對(duì)當(dāng)前的面向?qū)ο缶幊田L(fēng)格的排斥,因?yàn)槟鞘莻蚊嫦驅(qū)ο缶幊蹋^的偽面向?qū)ο螅褪侵竿ㄟ^(guò)單根繼承和針對(duì)接口或抽象類來(lái)寫(xiě)代碼,就以為是在進(jìn)行面向?qū)ο蟮木幊蹋菍?shí)在是太天真了。設(shè)計(jì)模式中的那些點(diǎn)子,用得好,確實(shí)能解決偽面向?qū)ο蟮囊恍﹩?wèn)題,無(wú)可厚非;用得不好,無(wú)緣無(wú)故地引入一些不必要的東西,模式的應(yīng)用意味著間接性的增厚。現(xiàn)實(shí)中,能恰當(dāng)?shù)赜煤媚J降娜耍僦稚伲J娇偸浅霈F(xiàn)在一些不必要出現(xiàn)的場(chǎng)合下。很多人都是生吞活剝了23種模式之下,內(nèi)心就沾沾自喜,到處躍躍欲試,鮮有人嘗試?yán)斫饽J奖澈蟮慕y(tǒng)一思想是什么,或者說(shuō),如果代碼本身就已經(jīng)能夠很好類與類之間的耦合問(wèn)題,可勝過(guò)千萬(wàn)種設(shè)計(jì)模式。
以下文字,與孟巖大俠的觀點(diǎn),存在部分重復(fù)之處,讀者認(rèn)為在下是在拾他的牙慧,我也不反對(duì),畢竟人家的文章反表在前,我無(wú)話可說(shuō),本文的重點(diǎn),旨在表達(dá)在下對(duì)設(shè)計(jì)模式的鄙視。
既然有偽面向?qū)ο螅陀姓婷嫦驅(qū)ο蟆U婷嫦驅(qū)ο蟮木幊蹋褪悄阒恢酪粋€(gè)對(duì)象的ID,其他的一切事情,它繼承自那些父類,實(shí)現(xiàn)了那些接口,統(tǒng)統(tǒng)一概都不得而知,然后你只能通過(guò)這個(gè)ID給對(duì)象發(fā)送消息,以此來(lái)驅(qū)使對(duì)象做一些事情,注意,確確實(shí)實(shí)是只是發(fā)送消息,而不是調(diào)用該對(duì)象的函數(shù),那怕是調(diào)用了該對(duì)象實(shí)現(xiàn)的接口函數(shù),也意味著該對(duì)象的依賴,好吧,說(shuō)錯(cuò)了,是對(duì)該接口的依賴,不管怎么樣,都是依賴,而且依賴接口,還搞得客戶代碼和對(duì)象都要依賴于同一個(gè)接口了。
給對(duì)象發(fā)送消息,聽(tīng)起來(lái)似乎有點(diǎn)抽象,但是,只要聯(lián)想到WINDOWS的窗口句柄和消息處理函數(shù),就自然明白是怎么回事了。在WINDOWS下,每一個(gè)窗口都是一個(gè)對(duì)象,窗口句柄代表了對(duì)象ID。操作窗口時(shí),只能通過(guò)SendMessage或PostMessage來(lái)讓窗口作一些事情。SendMessage或PostMessage的四個(gè)參數(shù),分別是對(duì)象ID、消息編號(hào)、消息參數(shù)1和消息參數(shù)2。客戶代碼在使用窗口時(shí),不需要假設(shè)什么接口,能做的,僅須做的,僅僅就是給它發(fā)送消息,客戶代碼完全無(wú)須依賴于什么鬼接口,非常簡(jiǎn)單明了。但是,這里存在一個(gè)問(wèn)題,消息參數(shù)1和消息參數(shù)2都是void*類型,不安全耶,要是誤發(fā)送錯(cuò)了消息,那該怎么辦。在偽面向?qū)ο笾校蛻粢部赡茉谡{(diào)用接口參數(shù)時(shí),也可能會(huì)傳錯(cuò)了參數(shù),只不過(guò)是編譯器可以幫你找出來(lái)。其實(shí)類型不安全,也沒(méi)什么,客戶既然要發(fā)送消息給對(duì)象,自然要遵守使用的協(xié)議,自然要清楚自己在做什么事情,這本來(lái)就是C語(yǔ)言的精神,一切相信程序員。
好了,該是給WINDOWS大唱贊歌了。WINDOWS系統(tǒng)的窗口是我見(jiàn)過(guò)中算是比較象樣的面向?qū)ο蟮牡浞读恕⒚嫦驅(qū)ο蟮木褙瀼氐降祝翱趯?duì)象很好地做到了僅僅是純粹解析消息,執(zhí)行消息,與外界的其他對(duì)象,不存在任何一點(diǎn)耦合。一個(gè)窗口對(duì)象中的lpfnWndProc其實(shí)可以理解成指向虛函數(shù)表的指針,但是它卻要比虛函數(shù)表的指針功能更加強(qiáng)大靈活,并且還不存在虛擬函數(shù)表的種種問(wèn)題。強(qiáng)大之處,就之于lpfnWndProc相當(dāng)于指向一個(gè)龐大的虛函數(shù)表,這個(gè)表有2的32次方多個(gè)虛函數(shù),靈活之處可以突破虛函數(shù)的種種限制。當(dāng)你要給一個(gè)窗口對(duì)象添加新的功能,或者不滿意它的某些原有操作的時(shí)候,你完全可以子類化該窗口,在子類化操作中,截取關(guān)心的消息,解析消息,執(zhí)行消息,至于其他的消息,直接交給lpfnOldWndProc或DefWindowProc就是了。從這個(gè)意義上講,所有的窗口對(duì)象繼承于DefWindowProc,而子類化窗口即是繼承于lpfnOldWndProc,但是,這里的繼承,卻沒(méi)有偽面向?qū)ο笾械睦^承或?qū)崿F(xiàn)接口的種種弊端。而且,在你子類化窗口的時(shí)候,不用影響到客戶的任何一點(diǎn)點(diǎn)代碼,客戶代碼依舊是發(fā)送消息,卻不知道窗口對(duì)象已經(jīng)舊貌換新顏了,OH,這實(shí)在太美妙了。所有的耦合,奇跡般的消失得干干凈凈了。消息比之于接口,那完完全全是純粹的正交關(guān)系,并且沒(méi)有接口的種種累贅,種種缺陷。并且更爽的是,即使對(duì)象沒(méi)有處理消息,也沒(méi)有關(guān)系,客戶代碼依然可以給對(duì)象發(fā)送消息。
從某種意義上講,設(shè)計(jì)模式不過(guò)是為了解耦對(duì)象與對(duì)象之間的耦合關(guān)系,當(dāng)對(duì)象之間不存在耦合的時(shí)候,設(shè)計(jì)模式還有什么存在的意義嗎?以下結(jié)合WINDOWS系統(tǒng)來(lái)理解,所謂的設(shè)計(jì)模式,不過(guò)是消息發(fā)送的一些應(yīng)用罷了。下面的舉例,例子之間沒(méi)有什么必然關(guān)系,我想到那里就寫(xiě)到什么,模式后面似乎應(yīng)該帶上英文,但我也懶得寫(xiě)了。
觀察者模式:一個(gè)廣播消息就搞定了;
模板方法:不過(guò)是按順序給一個(gè)對(duì)象發(fā)送某些指定的消息而已;
工廠方法、抽象工廠:用一個(gè)或幾個(gè)lpClassName就搞定了;
原型:不過(guò)是發(fā)送一條WM_COPYOBJECT的消息而已;
裝飾者或者狀態(tài):嘿,子類化就完成了,并且非常徹底,一點(diǎn)都不覺(jué)得別扭;
命令:對(duì)象將SendMessage中的消息編號(hào)、消息參數(shù)1和消息參數(shù)2保存起來(lái)就是了,這沒(méi)什么大不了的;
策略:不過(guò)一個(gè)回調(diào)函數(shù),外加必要的參數(shù);
橋接:貌似沒(méi)什么必要;
……
沒(méi)有了!落得個(gè)一片白茫茫大地真干凈!
posted on 2012-05-31 17:59 華夏之火 閱讀(2230) 評(píng)論(8) 編輯 收藏 引用