開閉原則的意思是軟件實體應該對擴展開發,對修改關閉(Software entitIEs should be open for extension,but closed for modification)。實現開閉原則的途徑是抽象,將需要擴展的部分抽象出來,並留出擴展接口。打個比方,比如電腦機箱上有usb的插口,這些插口就是可擴展的部分,我們可以在這些usb插口上插上鼠標,鍵盤,U盤,還可以插上網銀的U盾等等。電腦硬件上對於usb接口的這個設計就是一個符合開閉原則的設計。
為什麼要遵循開閉原則呢?因為開閉原則可以使軟件系統更容易復用,更容易維護,當某個軟件實體,不適合了,我可以重新做另外一種實現,並將現有的實現替換掉。比如說統計個稅的算法發生了一些變化,我可以在不改變原有代碼的情況下,重新實現一個算法將原有的算法替換下來。比如說殺毒軟件,在出現一種新的病毒時,開發出一個查殺這種病毒的新模塊,可以只開發更新這個查殺模塊,而不需要改變原有系統的內容。
開閉原則這麼好,如何實現符合開閉原則的軟件系統呢?答案是抽象,將可能發生變化的功能點進行抽象,並留出變化的接口。設計模式中很多模式都可以幫我們實現開閉原則,個人的理解設計模式是對抽象用法的一種總結。
其實我們在項目已經為開閉原則做了一些工作了,比如說我們進行三層開發,將數據層抽象出來,並定義個數據處理的接口,我們可以通過新開發一個數據層把剛開始將數據存放到SQL Server中的實現,修改為將數據存放到my sql中的實現;我們將業務邏輯中的代碼從UI代碼中分離出來,這就為我們復用業務邏輯的代碼提供了可能,我們可以開發一個專門為手機使用的UI層出來,當用戶用手機訪問我們的系統時,智能的切換到手機UI層的代碼上去執行。
實現開閉原則的例子,其實我都不好意思自己舉例子了,因為我正在使用Office 2007寫這篇博客,在Office2007的快捷工具欄中就有一項是加載項,就是說Office 2007能將插件加載進來使用,如下圖所示:
Snagit在Word中添加了一個插件,這種插件技術就是一種遵循OCP的實現;再說我們整天使用的Visual Studio 它的可擴展程度更高,可以開發很多類型的工具對他進行擴展。
為了本文的完整性,我還是厚著臉皮,用重構的方式舉一個遵循開閉原則的微不足道的實現。
下面的舉例實現的場景是個稅的計算:我的第一個版本是這樣子的
- class Program
- {
- static void Main(string[] args)
- {
- float salary = 10000;
- Console.WriteLine("收入是{0}的人應繳個稅是{1},",salary, GetTax(salary));
- }
- static float GetTax(float salary)
- {
- return (float)(salary * 0.03);
- }
- }
這個版本中我未做任何抽象,直接調用靜態方法算了,可是一不小心開兩會了,個稅要調整了,於是個稅的算法要進行調整了,怎麼辦呢,因為要少繳稅,我很高興的就要來重構上面的代碼了,既然個稅的計算方法是一個變化的東西,我就把它抽象出來吧。
- class Program
- {
- static void Main(string[] args)
- {
- float salary = 10000;
- Console.WriteLine("收入是{0}的人應繳個稅是{1},",salary, GetTax(salary));
- }
- static float GetTax(float salary)
- {
- ITaxCalculateStrategy strategy = GetTaxCalculateStrategy();
- return strategy.GetTax(salary);
- }
- /// <summary>
- /// 獲得應該使用的個稅計算方法
- /// </summary>
- /// <returns>個稅計算方法實現實例</returns>
- static ITaxCalculateStrategy GetTaxCalculateStrategy() {
- string typeName = ConfigurationManager.APPSettings["TaxCalculateStrategyType"];
- if (string.IsNullOrEmpty(typeName))
- throw new ConfigurationErrorsException("請配置TaxCalculateStrategyType");
- Type type = Type.GetType(typeName);
- if (type == null) throw new ConfigurationErrorsException("TaxCalculateStrategyType錯誤");
- return (ITaxCalculateStrategy)Activator.CreateInstance(type);
- }
- }
- /// <summary>
- /// 定義個稅計算的接口
- /// </summary>
- public interface ITaxCalculateStrategy
- {
- float GetTax(float salary);
- }
- /// <summary>
- /// 兩會前個稅計算辦法的實現
- /// </summary>
- public class TaxCalculateBefore2Conference : ITaxCalculateStrategy
- {
- float ITaxCalculateStrategy.GetTax(float salary)
- {
- return (float)(salary * 0.03);
- }
- }
- /// <summary>
- /// 兩會後個稅的計算方法
- /// </summary>
- public class TaxCalculateAfter2Conference:ITaxCalculateStrategy
- {
- float ITaxCalculateStrategy.GetTax(float salary)
- {
- return (float)(salary * 0.020);
- }
- }
因為要少繳稅,所以我很愉快的重構了之前的代碼,可以轉眼兩會開完了,結果並非如我預期的個稅變化,咋辦呢?沒關系我們重新開發一個個稅計算方法,修改下配置就可以仍舊使用之前的個稅計算辦法了。
開閉原則實現的關鍵點在於抽象,也許我們剛開始不知道該把那部分抽象出來,但是這並不是問題,我們可以遵循簡單設計的原則,當變化來了的時候,再重構代碼,做到一種滿足開閉原則的設計。
切忌到處都抽象,如果到處都抽象就會導致系統過度設計,過度復雜。這反而是不利於系統的維護。完全的開閉原則是不可能實現的,所以請保持簡單設計,在需要的時候做符合開閉原則的設計。
通過本文對開閉原則的介紹,希望對你有幫助。
【編輯推薦】