1. 程式人生 > 其它 >設計模式學習--面向物件的5條設計原則之開放封閉原則--OCP

設計模式學習--面向物件的5條設計原則之開放封閉原則--OCP

一、OCP簡介(OCP--Open-Closed Principle): Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification。 軟體實體應當對擴充套件開放,對修改關閉,即軟體實體應當在不修改(在.Net當中可能通過代理模式來達到這個目的)的前提下擴充套件。 Open for extension:當新需求出現的時候,可以通過擴充套件現有模型達到目的。 Close for modification:對已有的二進位制程式碼,如dll,jar等,則不允許做任何修改。 二、OCP舉例: 1、例子一 假如我們要寫一個工資稅類,工資稅在不同國家有不同計算規則,如果我們不堅持OCP,直接寫一個類封裝工資稅的算稅方法,而每個國家對工資稅的具體實現細節是不盡相同的!如果我們允許修改,即把現在系統需要的所有工資稅(中國工資稅、美國工資稅等)都放在一個類裡實現,誰也不能保證未來系統不會被賣到日本,一旦出現新的工資稅,而在軟體中必須要實現這種工資稅,這個時候我們能做的只有找出這個類檔案,在每個方法里加上日本稅的實現細節並重新編譯成DLL!雖然在.NET的執行環境中,我們只要將新的DLL覆蓋到原有的DLL即可,並不影響現有程式的正常執行,但每次出現新情況都要找出類檔案,新增新的實現細節,這個類檔案不斷擴大,以後維護起來就變的越來越困難,也並不滿足我們以前說的單一職責原則(SRP),因為不同國家的工資稅變化都會引起對這個類的改變動機!如果我們在設計這個類的時候堅持了OCP的話,把工資稅的公共方法抽象出來做成一個介面,封閉修改,在客戶端(使用該介面的類物件)只依賴這個介面來實現對自己所需要的工資稅,以後如果系統需要增加新的工資稅,只要擴充套件一個具體國家的工資稅實現我們先前定義的介面,就可以正常使用,而不必重新修改原有類檔案! 2、例子二 下面這個例子就是既不開放也不封閉的,因為Client和Server都是具體類,如果我要Client使用不同的一個Server類那就要修改Client類中所有使用Server類的地方為新的Server類。


class Client
{
   Server server;
   void GetMessage()
   {
      server.Message();
   }
}
 
class Server
{
   void Message();
}

下面為修改後符合OCP原則的實現,我們看到Server類是從ClientInterface繼承的,不過ClientInterface卻不叫ServerInterface,原因是我們希望對Client來說ClientInterface是固定下來的,變化的只是Server。這實際上就變成了一種策略模式(Gof Strategy)

interface ClientInterface
{
    public void Message();
    //Other functions
}
 
class Server:ClientInterface
{
    public void Message();
}
 
class Client 
{
   ClientInterface ci;
   public void GetMessage()
   {
       ci.Message();
   }
   public void Client(ClientInterface paramCi)
   {
       ci=paramCi;
   }
}
 
//那麼在主函式(或主控端)則
public static void Main()
{
   ClientInterface ci = new Server();
   //在上面如果有新的Server類只要替換Server()就行了.
   Client client = new Client(ci);
   client.GetMessage();
}

3、例子三 使用Template Method實現OCP:

public abstract class Policy
{
    private int[] i ={ 1, 1234, 1234, 1234, 132 };
    public bool Sort()
    {
        SortImp();
    }
    protected virtual bool SortImp()
    {
 
    }
}
 
class Bubbleimp : Policy
{
    protected override bool SortImp()
    {
        //氣泡排序
    }
}
class Bintreeimp : Policy
{
    protected override bool SortImp()
    {
        //二分法排序
    }
}
 
//主函式中實現
static void Main(string[] args)
{
    //如果要使用氣泡排序,只要把下面的Bintreeimp改為Bubbleimp
    Policy sort = new Bintreeimp();
    sort.Sort();
}
 

三、OCP優點: 1、降低程式各部分之間的耦合性,使程式模組互換成為可能; 2、使軟體各部分便於單元測試,通過編制與介面一致的模擬類(Mock),可以很容易地實現軟體各部分的單元測試; 3、利於實現軟體的模組的呼喚,軟體升級時可以只部署發生變化的部分,而不會影響其它部分; 四、使用OCP注意點: 1、實現OCP原則的關鍵是抽象; 2、兩種安全的實現開閉原則的設計模式是:Strategy pattern(策略模式),Template Methord(模版方法模式); 3、依據開閉原則,我們儘量不要修改類,只擴充套件類,但在有些情況下會出現一些比較怪異的狀況,這時可以採用幾個類進行組合來完成; 4、將可能發生變化的部分封裝成一個物件,如: 狀態, 訊息,,演算法,資料結構等等 , 封裝變化是實現"開閉原則"的一個重要手段,如經常發生變化的狀態值,如溫度,氣壓,顏色,積分,排名等等,可以將這些作為獨立的屬性,如果引數之間有關係,有必要進行抽象。對於行為,如果是基本不變的,則可以直接作為物件的方法,否則考慮抽象或者封裝這些行為; 5、在許多方面,OCP是面向物件設計的核心所在。遵循這個原則可帶來面向物件技術所聲稱的巨大好處(靈活性、可重用性以及可維護性)。然而,對於應用程式的每個部分都肆意地進行抽象並不是一個好主意。應該僅僅對程式中呈現出頻繁變化的那部分作出抽象。拒絕不成熟的抽象和抽象本身一樣重要;