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