開放-封閉原則(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 的最常見的方法。可以把一個功能的通用部分和實現部分清晰的分離開來。