一 前瞻
空模式是設計模式的一種扇雕,設計模式是著名的四位程序設計大牛共同提出的,針對面象對象編程的一些相當通用的“思想”,甚至可以說是“過程”蝉揍,只要略微實現一下就可以在應用中跑出來的“過程”庸娱。毫不客氣地說着绊,如果沒有把《設計模式》讀到一定的深度,哪怕是十年二十年的資深程序員熟尉,從根子上說也是有嚴重欠缺的归露。
百度空模式,可以得到很多類似的文章斤儿,這并不奇怪剧包,它們很可能源自同一篇大牛的文章;因此對于中間發(fā)生的細節(jié)往果,是語焉不詳的疆液。求人不如求己,既然已經是半桶水了陕贮,那么把理解到的東西搞得扎實一些堕油,更扎實一些,也是題中應有之義。順便說一句掉缺,我一直記得GOF的《設計模式》里面是提過空模式的......當然我看這本書太早了卜录,記憶出錯也是很正常的,這并不是那么重要眶明。
二 要解決的問題
大家都抄書艰毒,我也抄個例子--就是那個向圖書館借書的例子。偽碼就不寫了搜囱,用文字描述一遍就好:某圖書館有圖書若干丑瞧,編號為從1開始,各不相同的自然數蜀肘;現在要開發(fā)一個接口嗦篱,用圖書編號為參數傳入,返回圖書的信息以供后期使用幌缝。這個問題還可以再抽象一些灸促,但是那樣會不太直觀,所以先這樣寫著涵卵。
以面向對象的態(tài)度來看浴栽,當然是設計一個書本類Book,給他一個以圖書編號為參數的構造函數New轿偎,傳入參數返回對應編號的實體對象ConcreteBookxxx典鸡。但是如果這個傳入參數不符合規(guī)范(比如給出了字符參數)或者參數是正確的數字,但庫中沒有這個數字對應的書本坏晦,該怎么處理萝玷?前者可以用數據類型檢查來解決,但后者呢昆婿?
這實際是個錯誤(或者例外)處理問題球碉。你或者可以無情地拋出例外,再使用一段通用的例外處理來做這件事仓蛆?但對于小型的應用睁冬,這未免太浪費資源了--無論是人力資源還是系統(tǒng)資源。另外看疙,拋出例外通常也意味著代碼的邏輯存在某些不確定性豆拨,或者說,不夠健壯能庆。
另一個選擇施禾,是在發(fā)現例外的時候返回null。這可以說是每一個程序員都做過或者正在做的事搁胆。這么做和拋出例外相比弥搞,可說有好有壞邮绿;好處是讓調用的用戶“知道這個調用的返回是有問題的”,壞處則是讓調用的用戶“不知道有什么問題”拓巧。當然對小規(guī)模代碼來說斯碌,這個壞處甚至不算是壞處--因為甚至不需要多人之間的協作就能完成一死;但對于工業(yè)級代碼肛度,這可能存在問題。于是空模式登場了投慈。
三 應用
空模式的作用承耿,就是在不返回null也不拋出例外的情況下,盡可能地把控制權交給被調用者(代碼)伪煤,同時調用者(用戶)也能更多地了解到例外的細節(jié)加袋。這兩者聽起來有些矛盾,讓我們做起來看看抱既。
還是上面那個借書的問題职烧,我們用空模式來改寫,看是什么樣子:因為我們不能返回null防泵,所以要先為Book類設計一個返回布爾值的判空函數IsNull()蚀之,并在缺省情況下返回true;再設計一個繼承于Book的新類NullBook捷泞,重載這個IsNull()函數將它返回false足删。最后,當傳入構造函數Book:New()的參數不符合規(guī)范時锁右,就返回一個NullBook對象失受。
這樣做的好處是,無論給出什么樣的輸入咏瑟,都可以“正撤鞯剑”返回一個Book對象;你只要通過Book:IsNull()就可以判定它是不是有效的Book--當然码泞,我們剛才通過判定null也能做到這一點谆焊,但NullBook對象還可以在輸入參數不符合規(guī)范時將這個“不符合”的上下文用某種方式保存下來(比如私有變量)并且輸出給用戶看(以你想得到的方式),這就是返回null永遠也做不到的事了浦夷。
四 新的問題
其實你已經看到了辖试,就是“空模式”不一定要使用--只要你確定“永遠也不需要”返回什么東西給調用者,而調用者也只希望用最簡單的判定null來處理即可劈狐。這在小規(guī)模的代碼中是很常見的罐孝;永遠也不要為了用模式而用模式,不是每個釘子都需要錘子敲幾下的肥缔,你可能弄巧成拙莲兢。
除了代碼量增加之外,另一個問題就是上面的代碼會產生很多的NullBook空對象。這個的處理需要用到另一個設計模式“單件”改艇,也可以用靜態(tài)對象來配合處理收班,和本文關系不密切,就不在這里細說了谒兄。
五 其他
這是在對我很喜歡的打譜軟件multigo的模仿過程中得到的一些感悟摔桦。設計模式是非常好的東西,如果可能承疲,以后還會繼續(xù)寫邻耕,繼續(xù)思考,也希望能繼續(xù)進步--不管多小的進步燕鸽,都是好的兄世。