天天看點

設計原則利劍二--裡氏替換原則

英文名稱:Liskov Substitution Principle(LSP)

中文名稱:裡氏替換原則

原      則:所有引用基類的地方必須能透明地使用其子類的對象

               1、子類必須完全實作父類的方法

               2、子類可以有自己的個性

               3、覆寫或實作父類的方法時輸入參數可以被放大

               4、覆寫或實作父類的方法時輸出結果可以被縮小

作       用:讓繼承的利大于弊

真       言:時時刻刻記住,在使用父類的時候,随時要想到被子類替換後是否産生錯誤,是以必須要遵守以上四個原則

理論實踐:

             這個設計模式,看了2遍我才看懂意思,特别是什麼覆寫後參數的放大,傳回結果的縮小,完全被蒙了,其實仔細想想,由于在使用父類的地方可以随時替換成子類,那麼自然會出現很多的問題,就自然有了上面條件的限制。按照這個意思來了解,覺得有點像中國式家庭父子關系,父親的崗位可以替換給子女,但是子女首先必須要會父親的業務,也允許有一些自己的創新,父親有了大愛,随時将自己的工作崗位貢獻出來了,那麼子女當然也要有所付出,也要受到某些限制,如父親工作的崗位一些預設的潛規則,你仍然得去遵守,盡管你有自己的方法,如果沒有遵守父親的規則去做事情,那麼這件事情的成果也必須要小于或等于用父親的方法做事的成果,可以了解為給父親面子吧,嘿嘿。這也許就是繼承所必須要付出和注意的地方。

             用CS遊戲中來做個簡單的例子,每個士兵都要有槍來殺敵人,槍有很多種類型,手槍,機槍,步槍等,那麼所有的槍都從AbstractGun繼承過來,那麼子類中必須要求全部實作Shoot方法,那麼在士兵類中,就不在乎使用的是什麼槍,直接用AbstractGun來實作方法的調用Shoot,而具體使用什麼槍,則可以動态調用,那麼此時我們就要考慮到了,如果規定每個士兵隻允許時用一種槍支,并且提前将槍支給士兵,那麼這個時候在士兵類中就可以直接使用子類了,必須要設計的時候注意替換不會引起程式的錯誤。具體UML圖如下:

          上面的設計方式,無論如何替換成子類都不會出現問題,但是此時想想,如果出現了一個玩具槍,給士兵用來練習的,卻不能真正在戰場上殺敵,但是玩具槍實作不了殺敵人的任務,是以這裡不應該将玩具槍放到AbstractGun下面做為子類,即使作為了子類,shoot方法并不能真正殺死敵人,那麼士兵在戰場的結果就會是死。當然也可以在士兵子產品進行判斷,判斷配發的是否是玩具槍,但是一旦更改了士兵類,那麼很多相關的類都會受到影響,如這個士兵類下面很定還會有各種等級的士兵,都必須要做相應的改變。另外,如果是子類,很定會有shoot方法,那麼很定會給士兵傳遞錯誤的資訊。是以将玩具槍不要定義為槍,新的設計方式如下:

      按照此次裡氏設計原則,就将玩具槍從槍裡面徹底分離出來了,我們可以了解為玩具更好,不要了解為槍。要不然會誤大事。在這裡讓我想到自己正在開發的一個項目,也有繼承,但是好似沒有遵守這個原則,這個類主要用來處理使用者資料互動,如重新整理,導出資料等操作,其實首頁與圖形報表根本就不需要上述所說的功能,說到這裡這個類連第一個單一職責原則都沒有遵守,将資料傳輸方法應該放入到另外一個類中去實作,這樣的操作方式,傳遞給最上層就是每個子產品都有此功能,那麼最上層還以為此表有這些操作,雖然這個設計在系統中并不會造成任何影響,但是如果改變一種設計方式,可以讓使用者體驗更好,如果是加載圖形首頁的時候,我就讓最上層的類不要去處理導出事件,快捷鍵事件,我現在系統的設計如下:

          經過重新設計,将HomePageControl,GraphPageControl獨立成2個類,這樣每次新增加一個子類的時候沒有必要去修改MainForm類了,實作UML圖如下:

繼續閱讀