設計原則之——開閉原則

開閉原則是設計原則基礎的基礎,其它原則均圍繞開閉原則進行展開。開閉原則也就是一個軟件實體應當對擴展開放,但對修改關閉。滿足了開閉原則的設計,我們的系統將達到在設計穩定的基礎上,方便的對軟件進行擴展,插入新的功能模塊的目的。

    怎麼樣做的開閉原則呢?抽象化是關鍵,也是我們經常聽到的“面象接口編程”,具體一點就是聲明的變量的類型、函數的參數類型、函數的返回類型等要儘量使用抽象類和接口。開閉原則實際也是“對可變性的封裝原則”,那就是“考慮你的設計中什麼可能發生變化,找到一個系統的可變因素,將它封裝起來”。把變化封閉起來也就是我們經常用到的繼承,編寫一個抽象類用子類來繼承他,或者編寫一個接口讓子類去實現他。繼承固然很好,既封裝了變化又實現了複用,但卻不能濫用,也就是說應當把繼承僅僅當做封裝變化的方法,而不應當被認爲是從一般的對象生成特殊的對象。就如我們爲了複用一個類裏面的函數,便用另一個毫不相干的類來繼承此類,這樣的繼承真可謂不倫不類。更糟糕一些那便是爲複用而複用,編寫一個抽象類去繼承一個毫不相干的另一個抽象類(這樣做可能是想複用此類中封裝的變化),這樣便把多於一種的可變性混合在了一起,混合的結果便是最終讓我們思維混亂。

    開頭我們說開閉原則是設計原則基礎的基礎,因爲其它的設計原則可以作爲實現開閉原則的手段和工具。

    1、里氏代換原則:任何基類可以出現的地方,子類一定可以出現。

    我們知道,實現開閉原則的關鍵是抽象化,而里氏代換原則中的基類和子類的繼承關係正是抽象化的具體體現,所以里氏代換原則是對實現抽象化的具體步驟的規範。違反里氏代換原則一個最經典的例子便是把正方形設計成長方形的子類。

    2、依賴倒轉原則:要依賴於抽象,不要依賴於實現。說的白一點就是要依賴於抽象類和接口不要依賴具體類,具體類也就是我們可以用new關鍵字實例化的類。依賴倒轉原則是實現開閉原則的一個手段。

    3、合成/聚合複用原則:要儘量使用合成/聚合,而不是繼承關係達到複用的目的。就如我們前面說的,如果爲了複用,便使用繼承的方式將兩個不相干的類聯繫在一起,這樣的方式是違反合成/聚合複用原則的,更進一步的後果那便是違反里氏代換原則。合成/聚合複用和里氏代換原則相輔相成,合成/聚合複用原則要求我們在複用時首先考慮合成/聚合關係,而里氏代換原則是要求我們在使用繼承時,必須滿足一定的條件。

    4、接口隔離原則:應當爲客戶端提供儘可能小的單獨接口,而不要提供大的總接口。

    5、迪米特法則:一個軟件實體應當儘可能少的與其他實體發生相互作用。

    一個和其它實體關係很少的實體,在系統擴展時受的影響就會很小。當然接口隔離原則也是同樣的道理,小的單獨的接口,在系統擴展時受到的影響也會很小,所不同的是,迪米特法則(廣義迪米特)限制的是相互作用的深度和廣度,而接口隔離原則限制的只是相互作用的廣度

    在網上看到“板橋里人”的一篇文章《你還在用ifelse嗎?》他在最後總結裏說:“將ifelse用在小地方還可以,如簡單的數值判斷。但是如果按照你的傳統習慣思維,在實現業務功能時也使用ifelse,那麼說明你的思維可能需要重塑,你的編程經驗越豐富,傳統過程思維模式就容易根深蒂固,想靠自己改變很困難;建議接受專業頭腦風暴訓練。”此文通篇是如何用設計模式來替換ifelse語句的說詞,不知道他這種極端的爲了設計模式而設計模式的做法,是不是被他所謂的“頭腦風暴”給刮瘋癲所導致的。我也說一下我極端的想法,如果一個極端到沒有任何可擴展性要求的系統,用面向對象的語言開發面向過程的程序,並無不可,面向對象裏面的類只不過是我們分類存放函數的一個容器,其他別無用處,如果這時你還要寫一大堆的接口和抽象類來顯擺你所瞭解的設計模式,這簡直就是強姦了設計模式,因爲設計模式是爲方便的擴展系統而生的,並不是讓你拿來顯擺的。關於各種對“將條件轉移語句改寫成爲多態性”的代碼重構做法的看法,我更傾向閻宏的的看法。

    上而講的“將條件轉移語句改寫成爲多態性”的代碼重構的做法有兩大缺點。

    第一大缺點,任何語言都提供條件轉移功能,如果拋棄語言本身的一些功能,那麼語言存在的價值又能體現在哪裏呢?難道一個面向對象的語言,面向對象的特性就是這個語言的全部嗎?條件轉移本身並不是錯誤的,也不是什麼罪惡,他更不是某些人眼裏面向過程語言向面嚮對象語言過渡時一個權宜的保留。如果需要,設計師完全可以選擇使用條件轉移ifelse,並且不需要去接受什麼頭腦風暴的摧殘。

    第二大缺點,使用多態性代替條件轉移意味着大量的類被創建出來。比如一個類如果有三個方法,每個方法都有一個三段的條件轉移語句,如果將它們都用多態性代替的話,就會造出九個不同的類。很難想象設計師怎麼能明白這九種組合成員之間的關係。

    那麼何時需要用多態性取代條件轉移語句呢?

    應當從“開閉”原則出發來做判斷。如果一個條件轉移語句確實封裝了某種業務邏輯的可變性,那麼將此種可變性封裝起來就符合“開閉”原則的設計思想。但是,如果一個條件轉移語句沒有涉及重要的業務邏輯,或者不會隨着時間的變化而變化,也不意味着任何的可擴展性,那麼它就沒有涉及任何有意義的可變性。這時候將這個條件轉移語句改寫成爲多態性就是一種沒有意義的浪費。閻宏將這種對多態性的濫用叫做“多態性污染”。如果再濫用一些設計模式,估計就應該叫“設計模式污染”了。

發佈了35 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章