天天看點

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

8.3.2.3 Liskov替換原則

隻是從名稱上來判斷,并不能成為泛化關系的最終證據。1988年,Liskov在“Data abstraction and hierarchy”文章中提出了一個判斷的标準,後來被稱為Liskov替換原則(LSP):

如果對于每個類型S的對象O1,都有類型T的對象O2,對于所有以T的形式定義的程式P,當O1被O2替換時,P的行為不變,那麼S是T的子類型。

很多書和文章中提到Liskov替換原則時,會以矩形和正方形(有時會換成橢圓和圓)的問題為例。

假設把正方形看作矩形的子類,如圖8-107。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-107 把正方形當作矩形的子類

設定某矩形的A邊長為4,再設定B邊長為5,按照設想,此時求面積應該得到4×5=20。如果用正方形代替矩形,要麼為了保持正方形的限制,最終得到的面積是5×5=25,要麼兩邊自由變化,正方形就不再是正方形了。

根據Liskov替換原則可以判斷出圖8-107不合适,但Liskov替換原則沒有解釋其中的原因。

Bertrand Meyer在“Object-Oriented Software

Construction”一書用契約的觀點解釋:子類操作的前置條件應該不強于超類,後置條件應該不弱于超類。

例如,構造一個矩形對象需要提供兩個邊長參數,對這兩個參數并無要求,而構造正方形對象卻要求這兩個參數必須相等,即,子類操作的前置條件強于超類,不合适。

我們僅從屬性的角度來看看。如果獨立描述矩形和正方形所需的屬性,可以得到圖8-108。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-108 模組化矩形和正方形的屬性

從圖8-108可以得知,正方形的屬性比矩形還少一個,把正方形作為矩形的子類是不合适的。

反過來,矩形倒是更像正方形的子類,複用邊長,再加一個邊長。Bertrand Meyer在“Object-Oriented Software

Construction”中就提到,某類庫的早期版本,就是讓矩形繼承正方形。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-109 摘自“Object-Oriented

Software Construction( Second Edition )”, Bertrand Meyer, 1997

要建立正确的關系,可以減弱超類的定義,由子類定義有幾個邊長屬性,如圖8-110。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-110 超類不定義有幾個邊長屬性

圖8-110中,正方形是矩形的子類,但不是自由矩形的子類。自由矩形、正方形和黃金分割矩形(邊長比為黃金分割比0.618····:1)等是互相不重疊的矩形子集(子類)。

也可以把A邊長和B邊長屬性保留在矩形中,正方形、黃金分割矩形如果适用特殊的規則,可以作為矩形的狀态。正方形、黃金分割矩形等沒有增加新的屬性,隻是要求A邊長和B邊長的值符合某個限制,也就是說,正方形、黃金分割矩形是屬性值組合中的一個子集的表征,這個就是狀态。如圖8-111。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-111 用狀态來表達不同矩形

8.3.2.4 警惕拼湊泛化

您可能注意到,以上我們盡量通過屬性(包括關聯)來解釋泛化關系。

雖然泛化帶來的好處如Liskov所說,是落在行為上——“P的行為不變”,但如果抛開屬性直撲行為,很可能會帶來“假泛化”、“假面向對象”。

如圖8-112,因為X和Y都有操作op1,是以泛化出A,把op1提上去成為抽象操作。這個沒有問題。問題出在前面,怎麼知道op1作為X和Y的操作是合适的?最終的依據還是X和Y的屬性(包括關聯)。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-112 操作怎麼來的,需要有依據

有一種偷懶遮羞布,就是胡亂安排操作,然後拼湊出泛化關系,根本不顧安排的操作是否合理。

一些“面向對象”實踐經常得到各種名稱最後有or或er的類(漢語則為“器”),就有這種嫌疑。開發人員可能一開始按照面向過程的思路噼裡啪啦把代碼寫出來,然後出于趕時髦需要“面向對象”,他就把過程名稱加上or或er作為類名稱,然後把原來的過程作為or或er類的操作。

當存在多個類似過程時,還可以加上一些泛化關系(或接口-實作)來做點綴,如圖8-113,這樣看起來就更有“面向對象”的味道了。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-113 帶有泛化關系的or或er類

可能在某些開發人員眼裡,圖8-113很有格調,可以用來吹噓的高大上詞彙有:OCP(開放-關閉原則)、DIP(依賴倒置原則)、模闆方法模式……等。這些由泛化關系衍生出來,被網紅圈子廣泛吹噓的原則和模式作用十分有限,指望了解被包裝出來的“SOLID原則”之類就能應對軟體複雜性,那真是太天真了。

*很多人可能是從Robert C. Martin的書《靈活軟體開發-原則、方法與實踐》中了解到OCP、DIP、LSP等。這些原則并非Robert C. Martin首先提出,而且和靈活過程沒有必然關系,但Robert C. Martin起這樣的書名,經常會導緻開發人員誤解這些是靈活人士提出來的。*

or或er類往往沒有屬性,隻有操作,大量的邏輯仍然隐藏在子類操作的實作中。當然,開發人員也可能覺得這是好事,“這說明我有算法啊!”——“算法”,又是一個可以吹噓的高大上詞彙。

和其他的偷懶遮羞布類似,這種做法一一對應,思考工作量小,還有各種高大上詞彙護法,于是開發人員洋洋得意,感覺自己已經很厲害了,連稱“受用”,紛紛去擁抱這種偷懶遮羞布。

or或er類有時會使用“政策模式”作為僞裝,如圖8-114,哇,我可以靈活組裝各種政策!順便再吹一通“組合優于繼承”之類,其實還是換湯不換藥,邏輯仍然隐藏在子類操作的實作中。

軟體方法(下)第8章分析之分析類圖—知識篇Part13-警惕拼湊泛化

圖8-114 “政策模式”換湯不換藥