《你必须知道的.NET》书中对OCP(开放封闭)原则的阐述
开放封闭原则(OCP,Open Closed Principle)是面向对象原则的核心。由于软件设计本身所追求的墓边就是封装变化,降低耦合,而开放封闭原则就是对这一目标的直接体现。(你必须知道的.NET p48页)
以下用例子说明,同样来自该书。
假设在一个柜台业务处理系统,首先有客户:
public class Client { public string Name{get;set;} private string ClientType { get; set; } }
ClientType表示客户的类型,如"存款用户","转账用户","取款用户"
public class BusyBankStaff { private readonly BankProcess bankProcess = new BankProcess(); public void HandleProcess(Client client) { switch (client.ClientType) { case "存款用户": bankProcess.Deposit(); break; case "转账用户": bankProcess.Transfer(); break; case "取款用户": bankProcess.DrawMoney(); break; } } }
业务处理类
public class BankProcess { public void Deposit() { } public void Transfer() { } public void DrawMoney() { } }
问题是,如果银行多了一种业务类型,比如代购公积金,那么,必然地,BankProcess要修改为
public class BankProcess { public void Deposit() { } public void Transfer() { } public void DrawMoney() { } public void BuyFund(){} }
,并且BusyBankStaff中坏味道的switch语句又要增加一个条件。
其实,这样类的设计违反了开放封闭原则。
所谓开放封闭,是指,对修改封闭——一个类一旦写好,就不能再修改;对扩展开放——如果有新的需求,可以在不修改原系统的基础上方便增加。
怎么改?抽象!对可能或经常变化的部分使用接口将其封装。在此例中,BusyBankStaff依赖于BankProcess类,将其改为依赖于一个IBankProcess接口
public interface IBankProcess { void Process(); }
然后,不同的业务类型都实现该接口
public class DepositProcess:IBankProcess { public void Process(){} }
public class TransferProcess:IBankProcess { public void Process(){} }
等等。
此时BusyBankStaff可以摇身一变,变成EasyBankStaff
public class EasyBankStaff { private readonly IBankProcess bankProcess = new BankProcess(); public void HandleProcess(Client client) { bankProcess=client.CreateProcess(); bankProcess.Process(); } }
注意到,业务的分配由银行的业务员转为客户,让客户自己依据自己的类型创建相应的bankProcess对象——这就是现实中客户依据业务类型的不同在排队取号机前取不同的业务号码。
现在,若新增一个业务,只要增加一个实现IBankProcess接口的类即可。
如
public class BuyFund:IBankProcess { public void Process(){} }
大功告成了吗?不!
若观察系统,我们会发现客户类此时已经转变为
public class Client { public string Name{get;set;} private string ClientType { get; set; } public IBankProcess CreateProcess() { switch(clientType) { case"存款用户": return new DepositProcess(); case "转账用户": return new TransferProcess(); } } }
又是一个违反OCP的类,又是switch的坏味道。
怎么办?同样是抽象!
将client类抽象为接口
public interface IClient { IBankProcess CreateProcess(); }
然后不同类型的客户都实现这一接口
public class DepositClient:Client { IBankProcess CreateProcess { return new DepositProcess(); } }
public class TransferClient:Client { IBankProcess CreateProcess { return new TransferProcess(); } }
如果有新的客户类型,那么就新增一个这样的类即可。完成!