介紹幾種特殊的類型設(shè)計(jì)
1、缺失行為的對象。
缺失行為,換一種特殊類型設(shè)計(jì)說法就是什么都不能的對象,如同我在上一篇文章中舉的石頭的例子一樣,沒有任何行為的對象是可以合理存在的。當(dāng)然,string的那個(gè)例子可能不夠好,因?yàn)閟tring或者說int等等純數(shù)據(jù)類型,不會(huì)出現(xiàn)在OOD中。但這種缺失行為的對象也并非罕見,常見于定義數(shù)據(jù)規(guī)范的對象:如ConnectionString、Uri、IPAddress,實(shí)際領(lǐng)域中,OA中的Document、Enterprise中的Order,用戶體系中Account、IIdentity、IPricipal這樣的對象,WebApplication中的Session。如果你仔細(xì)去找,這類型的對象其實(shí)非常多,在大多數(shù)領(lǐng)域中都有可能出現(xiàn)。
當(dāng)然,上面局的例子的每一種,某些東西的狂熱擁護(hù)者都能舉出反面例子,就像說明string也有方法一樣。但,方法不等于行為,正如同不會(huì)有人將屬性視為行為一樣,但屬性是方法來實(shí)現(xiàn)的。
給connectionString加上OpenConnection的方法或是給Account加上Logout的方法,抑或者Order的Seal或Sign方法,都不能說是一個(gè)好的Design,在我看來就是畫蛇添足。
2、缺失數(shù)據(jù)的對象。
缺失數(shù)據(jù),換一種特殊類型設(shè)計(jì)說法就是什么都沒有的對象。缺失數(shù)據(jù)不一定說是沒有成員變量,而是外部不可見,不可讀也不可寫。當(dāng)然也不排除真的沒有任何成員變量的情況,但這個(gè)不是問題的重點(diǎn)。正如同上一篇文章中某個(gè)回復(fù)說的一樣:“如果我們真的只需要一把刀,那么就造一把刀就可以了”。這樣的對象在OOD中也是存在的,比如說監(jiān)聽事件并主動(dòng)記錄的Logger,審核權(quán)限的Auditor,這些對象都不需要自己有什么數(shù)據(jù),別人也不關(guān)心他們有什么,只需要知道它們能干什么就行了。
3、行為和數(shù)據(jù)都沒有的對象。
換一種特殊類型設(shè)計(jì)說法什么都沒有,什么都不能的對象,這種情況比較極端,但只要類型有意義,即使行為和數(shù)據(jù)都沒有,這個(gè)類型本身就能構(gòu)成其存在的意義。除了標(biāo)記接口,這樣的對象極為罕見,舉一個(gè)可能并不是很恰當(dāng)?shù)膱鼍埃?/P>
有三種貨物:豬肉、魚肉和雞肉,然后他們都是一份份的,每一份豬肉、每一份魚肉以及雞肉都是一樣的,但豬肉、魚肉雞肉之間并不相同。
我們有幾個(gè)倉庫,然后倉庫有這么一個(gè)行為,把一份貨物放進(jìn)去,但每種貨物放進(jìn)去的時(shí)候都要引發(fā)不同的行為。
當(dāng)然,我們可以寫三個(gè)方法,但更好的做法是寫同一個(gè)方法的三個(gè)重載。當(dāng)然直到這里,必須設(shè)計(jì)成三種類型看起來仍然沒什么必要,但是如果我們再增添種種操作,例如倉庫之間可以調(diào)配貨物,根據(jù)貨物選擇加工員,設(shè)計(jì)為三種類型就能利用靜態(tài)聯(lián)編等手段大大的簡化代碼。
更進(jìn)一步,在以后可能會(huì)提出需求要跟蹤貨物在倉庫之間調(diào)配的過程并計(jì)算中間的不合理損耗,這樣的情形下,一開始設(shè)計(jì)成三種類型就會(huì)很有幫助,盡管在一開始看起來這是一件很傻的事情。換言之,這樣的對象在設(shè)計(jì)中存在的意義可能更多的是為以后重構(gòu)做準(zhǔn)備。
在設(shè)計(jì)中,可能更常見的是不關(guān)心對象的行為和數(shù)據(jù)。例如容器對象不關(guān)心里面儲(chǔ)存的容器項(xiàng)有什么行為和數(shù)據(jù),序列化器不關(guān)心被序列化的對象有什么行為和數(shù)據(jù)。我們做一個(gè)模塊的設(shè)計(jì),這個(gè)模塊就可能有不關(guān)心數(shù)據(jù)和行為的對象。
4、缺失標(biāo)識(shí)。
缺乏標(biāo)識(shí),即類型無意義的類,不應(yīng)出現(xiàn)在在設(shè)計(jì)中,在編程中可以靈活加以運(yùn)用。
常見的有,Math、HttpUtility為代表的工具類(函數(shù)集),只包含常量的常量集類。或許你可以說HttpUtility是“關(guān)于http的工具箱”,但Math卻很難去說這是什么東西,數(shù)學(xué)手冊?關(guān)于數(shù)學(xué)的工具箱?還是計(jì)算器?
廣義一點(diǎn),用于輔助實(shí)現(xiàn)的類,也可以認(rèn)為是類型沒有意義的,如Wrapper、Builder、Adapter、Factory等,因?yàn)樗鼈儽旧聿⒉皇且粋€(gè)什么東西,而是為了某個(gè)目的而創(chuàng)造出來的。或者說屬于編程技巧而不是設(shè)計(jì)技巧。行為、數(shù)據(jù)都沒有,類型也沒有意義的類可以認(rèn)為不存在。繼續(xù),談?wù)剮追N看起來不合常理的設(shè)計(jì):
5、派生類屏蔽基類方法。
應(yīng)該說這種情況不會(huì)常見,派生類與基類的關(guān)系是抽象的關(guān)系,基類必須比派生類更抽象。一般而言,具體的對象應(yīng)該比抽象的對象擁有更多的功能。但在某些特殊情況下,多出來的卻是限制,如同癱瘓的貓的例子一樣。因?yàn)樘厥馇闆r并不多見,遇到這種情況的時(shí)候,仔細(xì)思考抽象的基類被屏蔽的方法是不是一個(gè)普適的行為,例如所有寵物都會(huì)拿耗子顯然是荒謬的。如果被屏蔽的方法并不是一個(gè)普適的行為(例如沒有或者很少直接用基類對象來調(diào)用這個(gè)方法),考慮將其刪除或者修改為protected。但在任何時(shí)候都應(yīng)當(dāng)記住,派生類不一定要比基類多一些功能,基類必須比派生類抽象是繼承的原則,不能因?yàn)榕缮惞δ苌倩惞δ芏喽ヮ嵉估^承方向,繼承的目的不在于代碼重用。具體的例子其實(shí)很多,比如說HttpWebRequest等。
6、很少,甚至沒有代碼的派生類。
這是一個(gè)很有意思的話題,如同剛剛說的沒有數(shù)據(jù)和行為的對象一樣,一個(gè)對象只要類型有意義在很多時(shí)候就是存在的充分條件。沒有代碼的派生類意味著不會(huì)給基類增加任何功能。因?yàn)榕缮惻c基類的關(guān)系是具體和抽象的關(guān)系,只要類型攜帶的信息更具體了,即使不增加任何東西也是合理的。具體的例子,比如ASP.NET中的PlaceHolder。因?yàn)樗枰邆涞墓δ埽ǔ洚?dāng)其他控件的容器)已經(jīng)被基類Control完全的實(shí)現(xiàn)了。所以它就不需要任何代碼了。但它比Control具體卻是很顯然的。
幾種特殊類型設(shè)計(jì)的總結(jié)
OOD不是三言兩語,幾個(gè)原則,幾個(gè)案例就能說清楚的事情,理解,以及實(shí)踐加上漫長的過程才能從OOP的領(lǐng)域邁向OOD的境界。這篇以及上篇文章主要我所希望表達(dá)的,并不是想說OOD是什么玄之又玄,存乎一心的東西。其實(shí)想說的東西也很老套,任何一個(gè)規(guī)則都有其背景和場景,千萬不要認(rèn)為所有的規(guī)則都是普適的,搞清楚背后的故事很重要。
你有一個(gè)良好的編程習(xí)慣,懂得靈活運(yùn)用各種設(shè)計(jì)模式,這只是說明你具有一定的OOP水平了而已。正如同上面所說的,一個(gè)類(對象)只要其類型是有意義的,其存在就有意義,OOD要求從類型而不是其成員入手。這是一種抽象的藝術(shù),盡管我很希望能夠找到一句簡單的話讓大家可以一下子茅塞頓開從OOP一下跳到OOD的層次。但很可惜,我沒找到。。。。。
【編輯推薦】