前言:這篇繼續來看看開閉原則。廢話少說,直接入正題。
軟體設計原則系列文章索引
C#軟體設計——小話設計模式原則之:依賴倒置原則DIP
C#軟體設計——小話設計模式原則之:單一職責原則SRP
C#軟體設計——小話設計模式原則之:接口隔離原則ISP
C#軟體設計——小話設計模式原則之:開閉原則OCP
開閉原則,英文縮寫OCP,全稱Open Closed Principle。
原始定義:Software entities (classes, modules, functions) should be open for extension but closed for modification。
字面翻譯:軟體實體(包括類、子產品、功能等)應該對擴充開放,但是對修改關閉。
對擴充開放。子產品對擴充開放,就意味着需求變化時,可以對子產品擴充,使其具有滿足那些改變的新行為。換句話說,子產品通過擴充的方式去應對需求的變化。
對修改關閉。子產品對修改關閉,表示當需求變化時,關閉對子產品源代碼的修改,當然這裡的“關閉”應該是盡可能不修改的意思,也就是說,應該盡量在不修改源代碼的基礎上面擴充元件。
一般情況,我們接到需求變更的通知,通常方式可能就是修改子產品的源代碼,然而修改已經存在的源代碼是存在很大風險的,尤其是項目上線運作一段時間後,開發人員發生變化,這種風險可能就更大。是以,為了避免這種風險,在面對需求變更時,我們一般不修改源代碼,即所謂的對修改關閉。不允許修改源代碼,我們如何應對需求變更呢?答案就是我們下面要說的對擴充開放。
通過擴充去應對需求變化,就要求我們必須要面向接口程式設計,或者說面向抽象程式設計。所有參數類型、引用傳遞的對象必須使用抽象(接口或者抽象類)的方式定義,不能使用實作類的方式定義;通過抽象去界定擴充,比如我們定義了一個接口A的參數,那麼我們的擴充隻能是接口A的實作類。總的來說,開閉原則提高系統的可維護性和代碼的重用性。
下面就結合之前部落客在園子裡面看到的一個使用場景來一步步呈現使用實作類程式設計的弊端。
場景說明:馬上中秋節了, **公司希望研發部門研發一套工具,實作給公司所有員工發送祝福郵件。
接到開發需求,研發部立刻開會成立研發小組,進入緊張的開發階段,經過1個月的艱苦奮戰,系統順利上線。代碼實作如下:
一切都很順利,系統也得到公司好評。
日複一日,年複一年,随着時間的推移,公司發現郵件推送的方式也存在一些弊病,比如某些網絡不發達地區不能正常地收到郵件,并且在外出差人員有時不能正常收到郵件。這個時候公司上司發現短信推送是較好的解決辦法。于是乎,需求變更來了:增加短信推送節日祝福的功能,對于行政部等特殊部門保留郵件發送的方式。
研發部的同僚們雖然已有微言,但是沒辦法,也隻有咬着牙忙了,于是代碼變成了這樣。
經過一段時間的加班、趕進度。終于大功告成。
随着公司的不斷發展,很多産品、平台都融入了微信的功能,于是乎公司上司又希望在保證原有功能的基礎上增加微信的推送方式。這個時候研發部的同僚們就怨聲載道了,這樣一年改一次,何時是個頭?并且随着時間的推移,研發部員工可能發生過多次變換,現在維護這個系統的員工早已不是當初的開發者,在别人的代碼上面改功能,做過開發的應該都知道,簡直苦不堪言,因為你不知道别人哪裡會給你埋一個“坑”。并且在現有代碼上面改,也存在很大的風險,即使做好之後所有的功能都必須重新經過嚴格的測試。
事情發展到這裡,就可以看出使用實作類去程式設計,你會因為需求變更而死得很慘,這個時候我們就能看出遵守開閉原則的重要性了,如果這個系統設計之初就能考慮這個原則,所有的可變變量使用抽象去定義,可能效果截然不同。
如果項目設計之初我們定義一個ISendable接口,我們看看效果怎樣呢?
設計分析:在MessageService服務類中,我們定義了ISendable的接口變量m_oSendHelper,通過這個接口變量,我們就能很友善的通過擴充去應對需求的變化,而不必修改原來的代碼。比如,我們現在再增加一種新的推送方式,對于我們的MessageService服務類來說,不用做任何修改,隻需要擴充新的推送消息的工具類即可。從需要抽象的角度來說,開閉原則和依賴倒置原則也有一定的相似性,不過部落客覺得,開閉原則更加偏向的是使用抽象來避免修改源代碼,主張通過擴充去應對需求變更,而依賴倒置更加偏向的是層和層之間的解耦。當然,我們也不必分得那麼細,往往,一個好的設計肯定是遵循了多個設計原則的。
上面的設計,很好的解決了MessageService服務類中的問題,但是對于調用方(比如上文中的Main函數裡面),很顯然是違背了依賴倒置原則的,因為它既依賴接口層ISendable,又依賴接口實作層EmailMessage、PhoneMessage等。這肯定是不合适的。我們引入MEF,稍作修改。
Main函數裡面
如果你使用Unity,直接用配置檔案注入的方式更加簡單。
至此開閉原則的示例就基本完了。文中觀點有不對的地方,歡迎指出,部落客在此多謝了。如果園友們覺得本文對你有幫助,請幫忙推薦,部落客将繼續努力~~