1. 程式人生 > >開放-封閉原則(OCP)

開放-封閉原則(OCP)

開放-封閉原則(Open-Closed Principle OCP)

參考書籍

敏捷軟體開發 原則、模式與實踐
Agile Software Development Principles, Patterns, and Practices

問題1:怎樣的設計才能面對需求的改變卻可以保持相對穩定?

如果程式中的一處改動就會產生連鎖反應,導致一系列相關模組的改動,那麼設計就具有僵化性的臭味。 OCP 建議我們應該對系統進行重構,這樣以後對系統再進行那樣的改動時,就不會導致更多的修改。如果正確地應用 OCP ,那麼以後再進行同樣的改動時,就只需要新增新的程式碼,而不必改動已經正常執行的程式碼。

遵循 OCP 原則設計出的模組具有兩個主要的特徵:
1. 對於擴充套件是開放的 (Open for extension)
這意味著模組的行為是可以擴充套件的。當應用的需求改變時,我們可以對模組進行擴充套件,使其具有滿足那些改變的新行為。
2. 對於更改是封閉的 (Close for modification)
對模組行為進行擴充套件時,不必改動模組的原始碼或者二進位制程式碼。模組的二進位制可執行版本,無論是可連結的庫、DLL 或者 Java 的 .jar 檔案,都無需改動。

問題2:怎樣可能在不改動模組原始碼的情況下去更改模組的行為呢?怎樣才能在無需對模組進行改動的情況下就改變模組的功能呢?

關鍵是抽象
模組可以操作一個抽象體。由於模組依賴於一個固定的抽象體,所以它對於更改可以是關閉的。同時,通過從這個抽象體派生,也可以擴充套件此模組的行為。

一個簡單的不遵循 OCP 的設計


class Server
{
public:
    void doSomething()
    {
    }
}

class Client
{
public:
    void toDoSomething() 
    {
        Server s;
        s.doSomething();
    }
}


Server <– Client

Client類和Server類都是具體類。Client類使用Server類。如果我們希望Client物件使用另一個不同的伺服器物件,那麼就必須要把Client類中使用Server類的地方更改為新的伺服器類。

遵循 OCP 的設計
方法1:strategy模式


class ClientInterface    // 抽象
{
public:
    virtual void doSomething() = 0;
}

class Client
{
public:
    void setInterface(ClientInterface *pObj)
    {
        m_pObj = pObj;
    }  

    void toDoSomething()
    {
        if(m_pObj)
        {
            m_pObj->doSomething();
        }
    }

private:
    ClientInterface *m_pObj;
}

class Server : public ClientInterface
{
public:
    virtual void doSomething()
    {
    }
}    

class ServerNew : public ClientInterface
{
public:
    virtual void doSomething()
    {
    }
}

方法2:template method模式


class Client
{
public:
    virtual void doSomething() = 0;
    void toDoSomething()
    {
        doSomething();
    }
}

class Server : public Client
{
    virtual void doSomething()
    {
    }
}

這兩個模式是滿足 OCP 的最常見的方法。可以把一個功能的通用部分和實現部分清晰的分離開來。