開放封閉原則(Open-Closed Principle OCP)
Software entities(classes,modules,functions etc) should open for extension ,but close for modification.
什么意思呢?
所謂開放封閉原則就是軟件實體應該對擴展開放,而對修改封閉。開放封閉原則是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化,降低耦合,而開放封閉原則正是對這一目標的最直接體現。
開放封閉原則主要體現在兩個方面:
對擴展開放,意味着有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。
對修改封閉,意味着類一旦設計完成,就可以獨立其工作,而不要對類盡任何修改。
為什么要用到開放封閉原則呢?
軟件需求總是變化的,世界上沒有一個軟件的是不變的,因此對軟件設計人員來說,必須在不需要對原有系統進行修改的情況下,實現靈活的系統擴展。
如何做到對擴展開放,對修改封閉呢?
實現開放封閉的核心思想就是對抽象編程,而不對具體編程,因為抽象相對穩定。讓類依賴於固定的抽象,所以對修改就是封閉的;而通過面向對象的繼承和多態機制,可以實現對抽象體的繼承,通過覆寫其方法來改變固有行為,實現新的擴展方法,所以對於擴展就是開放的。
對於違反這一原則的類,必須通過重構來進行改善。常用於實現的設計模式主要有Template Method模式和Strategy 模式。而封裝變化,是實現這一原則的重要手段,將經常變化的狀態封裝為一個類。
以銀行業務員為例
沒有實現OCP的設計:
public class BankProcess
{ //存款
public void Deposite(){}
//取款
public void Withdraw(){ }
//轉賬
public void Transfer(){}
}
public class BankStaff
{
private BankProcess bankpro = new BankProcess();
public void BankHandle(Client client)
{
switch (client.Type)
{ //存款
case "deposite":
bankpro.Deposite();
break;
//取款
case "withdraw":
bankpro.Withdraw();
break;
//轉賬
case "transfer":
bankpro.Transfer();
break;
}
}
}
這種設計顯然是存在問題的,目前設計中就只有存款,取款和轉賬三個功能,將來如果業務增加了,比如增加申購基金功能,理財功能等,就必須要修改BankProcess業務類。我們分析上述設計就不能發現把不能業務封裝在一個類里面,違反單一職責原則,而有新的需求發生,必須修改現有代碼則違反了開放封閉原則。
從開放封閉的角度來分析,在銀行系統中最可能擴展的就是業務功能的增加或變更。對業務流程應該作為擴展的部分來實現。當有新的功能時,不需要再對現有業務進行重新梳理,然后再對系統做大的修改。
如何才能實現耦合度和靈活性兼得呢?
那就是抽象,將業務功能抽象為接口,當業務員依賴於固定的抽象時,對修改就是封閉的,而通過繼承和多態繼承,從抽象體中擴展出新的實現,就是對擴展的開放。
以下是符合OCP的設計:
首先聲明一個業務處理接口
public interface IBankProcess{ void Process();}
public class DepositProcess : IBankProcess
{
public void Process()
{ //辦理存款業務
Console.WriteLine("Process Deposit");
}
}
public class WithDrawProcess : IBankProcess
{
public void Process()
{ //辦理取款業務
Console.WriteLine("Process WithDraw");
}
}
public class TransferProcess : IBankProcess
{
public void Process()
{ //辦理轉賬業務
Console.WriteLine("Process Transfer");
}
}
public class BankStaff
{
private IBankProcess bankpro = null;
public void BankHandle(Client client)
{
switch (client.Type)
{ //存款
case "Deposit":
userProc = new DepositUser();
break;
//轉賬
case "Transfer":
userProc = new TransferUser();
break;
//取款
case "WithDraw":
userProc = new WithDrawUser();
break;
}
userProc.Process();
}
}
這樣當業務變更時,只需要修改對應的業務實現類就可以,其他不相干的業務就不必修改。當業務增加,只需要增加業務的實現就可以了。
設計建議:
開放封閉原則,是最為重要的設計原則,Liskov替換原則和合成/聚合復用原則為開放封閉原則提供保證。
可以通過Template Method模式和Strategy模式進行重構,實現對修改封閉,對擴展開放的設計思路。
封裝變化,是實現開放封閉原則的重要手段,對於經常發生變化的狀態,一般將其封裝為一個抽象,例如銀行業務中IBankProcess接口。
拒絕濫用抽象,只將經常變化的部分進行抽象。