本文列出了我在平時發現和積累的在面向對象編程中一些常見的“不夠面向對象”的情況。
需要指出兩點:
1.我們雖然列出了這九種情況,但并不是說出現了下面的情況就一定有問題了;我們希望讀者這可以將其作為一種信號——仔細考慮一下是不是有更好的設計。
2.我們這里所說的面向對象的對象特指領域對象,即對象中包含領域數據和業務邏輯。
要確定不夠面向對象的對象,首先要了解什么樣的對象算是面向對象的,或者說好的面向對象的對象。關于面向對象設計的原則從不同的角度有很多種說法,我們這里采用一種比較簡單的說法,即高內聚低耦合。所謂高內聚是指對象內的數據和方法是緊密相關的;所謂低耦合是指對象之間的依賴應當比較小,一個對象發生改變時不應當對不相關的對象產生影響。
一. 低內聚對象
我們把低內聚對象分為兩種:一種是應該屬于該對象的行為和數據分散到了其他對象中;另一種是該對象內部的行為和數據關系不夠緊密。下面的1、2是屬于前一種情況,3、4、5則是屬于后一種情況。
1.貧血對象(Anemic Object)
瞧,那條貧血的狗!
故事的發生是這樣的...
你養了一條寵物狗,在學習了面向對象編程之后,你打算為這條狗設計一個面向對象的系統。于是,根據你在C語言編程時的開發經驗,結合你對“封裝”二字的理解,你設計了這樣一條狗:)這條狗由四部分組成:頭、身子、腿和尾巴。
圖 1
隔壁住著一位面向對象大師——法號鑒摩,你拿著設計圖給他看。鑒摩大師只掃了一眼便說:
沒有行為的對象不是好對象。
你似懂非懂地點了點頭,正要往下說,大師揮了揮手說:“你明天再來罷。”
如果一個對象只有數據沒有行為,它就是一個貧血對象,它只能被別人操作,或者作為某個操作的結果。對于簡單的getter和setter,我們一般不將其歸為領域行為。所以,上面這個對象就是一個貧血對象。這條狗還不會叫、不會跑,甚至還不會搖尾巴討好你,真不知道你養這樣一條狗干啥。
處理貧血對象時可以考慮把操作對象數據的行為移動到這個對象里面。對數據的封裝只是面向對象中“封裝”這個概念的一部分,我們的對象中除了封裝數據還應當封裝行為。
對于跟物理世界一一對應的對象,一般來說,我們不容易犯這樣的錯誤。我們不妨來看一個實際工作中遇到的例子。在某個商店收銀系統中,有一個對象叫做Product,它被設計成這樣:
圖 2
這個Product就是一個貧血類。單純看這個類,是沒有什么問題的。我們需要結合其他的類來觀察。由于不同類型的產品打印方式不同,計稅規則也不同,所以我們還有一個處理Product的類: