面向?qū)ο蟮脑掝}本來是個老話題了。只是看到還有不少人對這個問題有所困惑,我也就不吝淺薄,談?wù)勛约簩γ嫦驅(qū)ο蟮睦斫狻_€請大家在讀過之后,能夠不計鄙人的淺薄,多提寶貴意見。不斷地爭論和討論是前進的根本動力。
面向?qū)ο蟮恼追椒ū緛砜梢苑譃槊嫦驅(qū)ο蠓治?、面向?qū)ο笤O(shè)計、面向?qū)ο缶幊痰取5窃谶@點上,我是贊同XP的開發(fā)思想的:代碼就是所有的設(shè)計。因此,我更愿意把面向?qū)ο罂醋饕粋€整體:一切最終落實到體現(xiàn)了面向?qū)ο笏枷氲拇a?;诖朔N考慮,在這里我也不區(qū)分OOA/OOD/OOP,而是泛指在面向?qū)ο笏枷胫笇?dǎo)下的軟件開發(fā)及實現(xiàn)全過程。需要注意的是:“體現(xiàn)了面向?qū)ο笏枷氲拇a”和“面向?qū)ο蟮拇a”是不同的概念, C#,java的語言特點決定了無論有沒有面向?qū)ο蟮撵`魂,其編碼的“肉體”都是面向?qū)ο蟮摹?/p>
關(guān)于OO的應(yīng)用場合,還有很多人在爭論。前些天看到還有人說三層結(jié)構(gòu)/多層結(jié)構(gòu)不適用于網(wǎng)站開發(fā)。從根本上講,這和OO是不是適用于網(wǎng)站開基本上是同一個問題。因為,很顯然,OO和N-Tier常常是一件事情的兩個不同方面,使用OO卻不使用多層結(jié)構(gòu)是難以想象的,使用N-Tier卻不使用OO那我就更加不知其可了。總之,在我看來,這兩件事情是緊密相連的。退一步講,將二者緊密聯(lián)系在一起,如果不是必然的話,至少也應(yīng)被看作是開發(fā)過程中的一個Best Practice。
下面談?wù)勎覍O + N-Tier的幾個基本觀點:
1)脫離了大量緩存和基于對象的業(yè)務(wù)邏輯的OO是沒有意義的。
我以前看到一些同道在進行OO開發(fā)的時候,對象常常只在數(shù)據(jù)加載和保存中的出現(xiàn)。對象更像是一個盒子,在需要數(shù)據(jù)時,我們建立數(shù)據(jù)庫連接,獲取結(jié)果集或者DataReader/ResultReader,將從數(shù)據(jù)庫獲得的數(shù)據(jù)“填充”到對象中,然后再將對象返回給上層應(yīng)用使用;在需要保存數(shù)據(jù)時,過程基本是對稱的:我們把對象中的數(shù)據(jù),一個字段一個字段讀出來,生成參數(shù)化的或者直接字符串拼接的查詢語句,建立數(shù)據(jù)庫連接,提交更新到數(shù)據(jù)庫。如果OO的主要內(nèi)容就是這些的話,OO如果不能說是完全沒有意義的話,起碼意義也不大。我們在付出了編寫了大量對象代碼并且在存儲加載時多一道手續(xù)的代碼和性能代價之后,得到的好處只是使上層應(yīng)用操作數(shù)據(jù)的代碼可讀性更好并且能夠進行一定的類型強制和檢查。這常常是很多開發(fā)人員對OO很困惑的原因:OO看起來很美,但是做了那么多事情,難道就是為了看起來很美嗎?這同時也成為一些編程老手把OO看作華而不實代名詞的原因:美是美了一點,但是代碼多了,性能差了,讓書呆子們?nèi)ビ冒伞V猿霈F(xiàn)這種局面,其根本原因在于“內(nèi)存對象世界”沒有提供太多的附加值。
大家應(yīng)該注意到“內(nèi)存對象世界”這一說法。在我看來,對象至少有兩個世界,一個是“持久化對象世界”,一個是“內(nèi)存對象世界”,這是由當今計算機的結(jié)構(gòu)特點決定的:如果數(shù)據(jù)要長期保存,數(shù)據(jù)就必須被保存到可持久化的媒介中;如果要進行運算,數(shù)據(jù)就必須被加載到可運算尋址的媒介中。前者就是DB Server等管理的硬盤、磁帶機...,后者就是內(nèi)存。在DB Server為中心的開發(fā)中,大家傾向于把所有邏輯直接放置在最接近“持久化對象世界”的DB Server中,并主要以存儲過程的形式存在。但是這樣就一定是最合理的嗎?尤其是對于網(wǎng)站應(yīng)用?如果這樣確實是合理的話,OO還有什么意義呢?是不是OO真的如某些人所說,只適合于圖形繪制等特定領(lǐng)域?要回答這些問題,我們還是要看看哪些情況下,“內(nèi)存對象世界”能夠相對獨立于“持久化對象世界”發(fā)揮其作用,這樣“內(nèi)存對象世界”就具備了獨立于“持久化對象世界”之外的獨立意義。
網(wǎng)站應(yīng)用的特點是:看數(shù)據(jù)的人多,創(chuàng)建數(shù)據(jù)的人少。眾所周知,恰恰就是這一點決定了緩存對于網(wǎng)站系統(tǒng)的重要性。對于主要以靜態(tài)內(nèi)容為主的小型簡易網(wǎng)站,我們在這里就沒有討論的必要了。真正有人氣的網(wǎng)站一定是具有動態(tài)增長的準靜態(tài)內(nèi)容(如新聞類網(wǎng)站,內(nèi)容不斷增加,但是本身很少修改)或者大量動態(tài)內(nèi)容(如交易型的電子商務(wù)網(wǎng)站)的。對于前一種情況,通常有兩種方式來加速其訪問,一種是生成靜態(tài)頁面,一種是在內(nèi)存中緩存頁面內(nèi)容。生成靜態(tài)頁面在性能上未必總是最好的選擇。只有當數(shù)據(jù)多到內(nèi)存中根本緩存不下,而這些數(shù)據(jù)又都有很大可能被用戶訪問時,生成靜態(tài)頁面才是較佳選擇。在數(shù)據(jù)較多,但是并發(fā)并不多時,或者并發(fā)雖多,但關(guān)注的內(nèi)容并不多時(如近兩日新增信息或者近幾日修改信息),頁面緩存就是更好的選擇。原因很簡單,因為頁面緩存的訪問速度要明顯快于靜態(tài)頁面。當然,有時候二者可以結(jié)合起來,這里就不多講了。對于后一種情況——真正的較大型電子商務(wù)性網(wǎng)站,我們面對的是另一個問題:一些信息被以網(wǎng)頁形式緩存,但這些信息本身的數(shù)據(jù)信息(如商品信息中的剩余數(shù)量,賣家Id等)常常需要被用到進行相關(guān)處理,如果只是進行了網(wǎng)頁緩存,而沒有進行對象緩存的話,緩存的意義就不打。在這種情況下,對象緩存就成為最好的選擇。其實,在前一種情況下,頁面緩存也可以以對象緩存的形式單獨或者分級混合并存。
回到我前面提出的觀點:“脫離了大量緩存和基于對象的業(yè)務(wù)邏輯的OO是沒有意義的”。其道理是顯而易見的,只有使用了大量緩存和基于對象的業(yè)務(wù)邏輯,建立一個OO結(jié)構(gòu)的收益才遠大于我們付出的代價,OO本身所在的“內(nèi)存對象世界”也才具備了脫離“持久化對象世界”而存在的根本意義。如果我們能把大量適合放在內(nèi)存中的業(yè)務(wù)邏輯搬移到應(yīng)用內(nèi)存中(而不是DB Server)的話,那效果就更好。當然也有一些操作不適合放在應(yīng)用邏輯中,如針對特別大量數(shù)據(jù)的非個性化的成批更新操作。至于什么叫大量,這取決于性能的考慮和實證。將很多業(yè)務(wù)邏輯從數(shù)據(jù)庫搬移到應(yīng)用內(nèi)存中是可行的,尤其是對于在執(zhí)行前要根據(jù)很有限的數(shù)據(jù)條目進行大量判斷來決定是否實質(zhì)性產(chǎn)生某種數(shù)據(jù)改變的邏輯更是如此。代價是速度可能較存儲過程差一點點,但是卻避免了因為大量不符合規(guī)則從而根本不會產(chǎn)生實質(zhì)數(shù)據(jù)改變的無效調(diào)用而導(dǎo)致的應(yīng)用服務(wù)器到數(shù)據(jù)服務(wù)器的網(wǎng)絡(luò)往復(fù),二者相比,無謂的往復(fù)往往代價要高得多。對于現(xiàn)實的電子商務(wù)網(wǎng)站,這一點是非常有價值的。
2)OO并不是低性能的代名詞,設(shè)計合理的OO會帶來意想不到的高性能。
OO并不是低性能的代名詞。恰恰相反,很多時候我都把OO當作我提升網(wǎng)站性能的基本武器:通過結(jié)構(gòu)合理而算法高效的對象緩存技術(shù)以及與對象結(jié)合并在同一地址空間中執(zhí)行的業(yè)務(wù)邏輯,我們往往能夠輕易地提升系統(tǒng)的性能。當然,一切的前提在于正確的設(shè)計,這不斷適用于OO,也適用于世間一切,沒有什么東西脫離了其實現(xiàn)的細節(jié)而萬歲千古!
以我們最近完成的一個較大型電子商務(wù)網(wǎng)站為例(目前日均訂單〉12000,成交訂單筆數(shù)在3000左右,而且最近增長勢頭很好),在運行近三個月后,該網(wǎng)站在Alexa的速度統(tǒng)計已經(jīng)從早先的very slow(平均一個頁面6~7妙),經(jīng)過slow(Alexa的數(shù)據(jù)是累計的,所以不是立即跳變),上升到average(平均一個頁面2.0s)。與此同時,DB Server的CPU Usage從原來的平均80%急劇的下降到平均10%!在現(xiàn)有系統(tǒng)中,基本上除了一些大批量數(shù)據(jù)移動和數(shù)據(jù)庫服務(wù)器段數(shù)據(jù)分頁查詢之外,所有業(yè)務(wù)邏輯均存在于我們的應(yīng)用邏輯中。這大概是違法很多朋友的開發(fā)直覺的。我經(jīng)常聽到的是:用存儲過程吧,為了性能...我不是在一切場合都反對使用存儲過程。但是我們必須清楚,每一種方法的優(yōu)點是什么?缺點是什么?條件是什么?什么場合適用?什么場合不會適用,什么場合二者應(yīng)該在更細微的場景/場合下分別或者混合使用。這有點象物理中的定理:誰見過沒有適用條件的定理?
3)如果你關(guān)注Scale out的能力,你就更應(yīng)該考慮OO。
現(xiàn)實世界的網(wǎng)站應(yīng)用系統(tǒng),都必須考慮Scalability問題,除非業(yè)務(wù)永不增長。只要業(yè)務(wù)會增長,使用人數(shù)不斷增多,服務(wù)器就一定會很快達到性能和并發(fā)極限。解決這個問題,通常只有兩個辦法:Scale up——買更好的服務(wù)器,而這往往因其代價過于高昂而不現(xiàn)實;Scale out——買更多的服務(wù)器,這往往是最終的實際選擇。但是Scale out始終面臨著數(shù)據(jù)集中(就算是拆分過的數(shù)據(jù)仍然各自相對集中,能做到無限隨意拆分嗎)的問題。如果大量的邏輯放在數(shù)據(jù)庫服務(wù)器一端,我相信很快數(shù)據(jù)庫服務(wù)器就會使得系統(tǒng)失去Scale out的能力和可能。因此,要保證Scale out的能力就必須保證數(shù)據(jù)庫(當然,必要的拆分策略也很重要,但那屬于另一個話題)只處理實質(zhì)性的數(shù)據(jù)提交和不可避免的數(shù)據(jù)查詢,對于能夠避免的數(shù)據(jù)查詢和非實質(zhì)性數(shù)據(jù)提交都應(yīng)該想辦法予以避免。這其實和我們上面所說的“脫離了大量緩存和基于對象的業(yè)務(wù)邏輯的OO是沒有意義的”剛好結(jié)合起來,成為一舉奪得的美事。
4)OO + N-Tier的基本思想乃至軟件開發(fā)方法的所有其他動力是對邏輯聚合和復(fù)用地不斷追求。
大家都知道,軟件開發(fā)的分析設(shè)計方法一直在不端演化,新的概念不斷涌現(xiàn)。軟件開發(fā)也從最早的直線條的機器語言,到面向過程的匯編語言,再到面向?qū)ο蟮膶ο蠡治鲈O(shè)計及編程(繼承、接口、多態(tài)),以及后來的DP(設(shè)計模式)、AOP(面向方面的編程)...。一系列的演化,確實讓人有眼花繚亂之惑何從入手之惑。那么在這一演化過程中,一以貫之的是什么?始終不變的又是什么呢?
在回答這個問題前,我們先來看一看MVC。MVC是現(xiàn)在大家都很熟悉的設(shè)計模式。那么到底什么是MVC呢?下面是Wikipedia關(guān)于MVC的解釋:Model-View-Controller (MVC) is a software architecture that separates an application‘s data model, user interface, and control logic into three distinct components so that modifications to the view component can be made with minimal impact to the data model component. 翻譯過來就是:MVC是一種將應(yīng)用的數(shù)據(jù)模型、用戶界面(視圖)以及控制邏輯分解到不同組件中的軟件構(gòu)架。這種分解可以使得視圖組件的細修改對數(shù)據(jù)模型只會產(chǎn)生很小的影響或者根本不產(chǎn)生影響,
在思考問題的時候,我喜歡跳出問題來看問題,在跨越時間甚至跨越領(lǐng)域的大跨度類比中不斷完成思想的升級及重構(gòu)?;氐角懊娴膯栴}。在軟件開發(fā)分析設(shè)計方法上,一以貫之的其實就是一種類似于MVC的思想:對問題域不斷進行重構(gòu),使得一種數(shù)據(jù)(不要狹隘的看待數(shù)據(jù),信息、規(guī)則、動作,一切都可以被看作是數(shù)據(jù))、該數(shù)據(jù)的管控邏輯、以及該數(shù)據(jù)的使用者( 不一定是UI,UI只是特例)能夠被區(qū)分開來。從這個角度來看待軟件開發(fā)領(lǐng)域的一切,你就會發(fā)現(xiàn),一切都在改變,一切也都沒有改變。我們只是為我們一以貫之的方法找到了的應(yīng)用發(fā)領(lǐng)域。如此而已。再簡化一點,實質(zhì)上,一切軟件設(shè)計方法的突破都是在于指明新的邏輯聚合方向,建立了新的邏輯聚合方法。OO如此,AOP如此,設(shè)計模式如此,一切都是如此。
5)只有一些很特定的情況不適用OO + N-Tier。
當然不是所有場合都必須或者適用OO + Np-Tier,因為二者的引入無疑會在某些方面增加成本,因此我們必須考慮我們付出的時候會得到什么,我們得到的時候又會失去什么?權(quán)衡——這實際上也是我們做一切事情的最基本思路,而不只是針對軟件開發(fā)。
大家可以自己權(quán)衡何時使用OO + N-Tier。下面是我認為不適合的一些場合:
a)太簡單的應(yīng)用,寫起來沒幾句代碼,使用OO + N-Tier根本不值得
b)已經(jīng)有一套很接近目標系統(tǒng)的原型系統(tǒng),暫時沒有必要使用OO + N-Tier,因為成本不合算
c)開發(fā)人員完全不知道該怎么使用OO + N-Tier,教育成本不低,暫時不建議使用
d)實時、密集信息處理,其處理過程非常簡單,連判斷都很少,使用OO + N-Tier根本沒必要