一、OCP簡介(OCP--Open-Closed Principle):
Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification。
軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉,即軟件實(shí)體應(yīng)當(dāng)在不修改(在.Net當(dāng)中可能通過代理模式來達(dá)到這個(gè)目的)的前提下擴(kuò)展。
Open for extension:當(dāng)新需求出現(xiàn)的時(shí)候,可以通過擴(kuò)展現(xiàn)有模型達(dá)到目的。
Close for modification:對(duì)已有的二進(jìn)制代碼,如dll,jar等,則不允許做任何修改。
二、OCP舉例:
1、例子一
假如我們要寫一個(gè)工資稅類,工資稅在不同國家有不同計(jì)算規(guī)則,如果我們不堅(jiān)持OCP,直接寫一個(gè)類封裝工資稅的算稅方法,而每個(gè)國家對(duì)工資稅的具體實(shí)現(xiàn)細(xì)節(jié)是不盡相同的!如果我們?cè)试S修改,即把現(xiàn)在系統(tǒng)需要的所有工資稅(中國工資稅、美國工資稅等)都放在一個(gè)類里實(shí)現(xiàn),誰也不能保證未來系統(tǒng)不會(huì)被賣到日本,一旦出現(xiàn)新的工資稅,而在軟件中必須要實(shí)現(xiàn)這種工資稅,這個(gè)時(shí)候我們能做的只有找出這個(gè)類文件,在每個(gè)方法里加上日本稅的實(shí)現(xiàn)細(xì)節(jié)并重新編譯成DLL!雖然在.NET的運(yùn)行環(huán)境中,我們只要將新的DLL覆蓋到原有的DLL即可,并不影響現(xiàn)有程序的正常運(yùn)行,但每次出現(xiàn)新情況都要找出類文件,添加新的實(shí)現(xiàn)細(xì)節(jié),這個(gè)類文件不斷擴(kuò)大,以后維護(hù)起來就變的越來越困難,也并不滿足我們以前說的單一職責(zé)原則(SRP),因?yàn)椴煌瑖业墓べY稅變化都會(huì)引起對(duì)這個(gè)類的改變動(dòng)機(jī)!如果我們?cè)谠O(shè)計(jì)這個(gè)類的時(shí)候堅(jiān)持了OCP的話,把工資稅的公共方法抽象出來做成一個(gè)接口,封閉修改,在客戶端(使用該接口的類對(duì)象)只依賴這個(gè)接口來實(shí)現(xiàn)對(duì)自己所需要的工資稅,以后如果系統(tǒng)需要增加新的工資稅,只要擴(kuò)展一個(gè)具體國家的工資稅實(shí)現(xiàn)我們先前定義的接口,就可以正常使用,而不必重新修改原有類文件!
2、例子二
下面這個(gè)例子就是既不開放也不封閉的,因?yàn)?span lang="EN-US">Client和Server都是具體類,如果我要Client使用不同的一個(gè)Server類那就要修改Client類中所有使用Server類的地方為新的Server類。
class Client
{
Server server;
void GetMessage()
{
server.Message();
}
}
class Server
{
void Message();
}
下面為修改后符合OCP原則的實(shí)現(xiàn),我們看到Server類是從ClientInterface繼承的,不過ClientInterface卻不叫ServerInterface,原因是我們希望對(duì)Client來說ClientInterface是固定下來的,變化的只是Server。這實(shí)際上就變成了一種策略模式(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;
}
}
//那么在主函數(shù)(或主控端)則
public static void Main()
{
ClientInterface ci = new Server();
//在上面如果有新的Server類只要替換Server()就行了.
Client client = new Client(ci);
client.GetMessage();
}
3、例子三
使用Template Method實(shí)現(xiàn)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()
{
//二分法排序
}
}
//主函數(shù)中實(shí)現(xiàn)
static void Main(string[] args)
{
//如果要使用冒泡排序,只要把下面的Bintreeimp改為Bubbleimp
Policy sort = new Bintreeimp();
sort.Sort();
}
三、OCP優(yōu)點(diǎn):
1、降低程序各部分之間的耦合性,使程序模塊互換成為可能;
2、使軟件各部分便于單元測(cè)試,通過編制與接口一致的模擬類(Mock),可以很容易地實(shí)現(xiàn)軟件各部分的單元測(cè)試;
3、利于實(shí)現(xiàn)軟件的模塊的呼喚,軟件升級(jí)時(shí)可以只部署發(fā)生變化的部分,而不會(huì)影響其它部分;
四、使用OCP注意點(diǎn):
1、實(shí)現(xiàn)OCP原則的關(guān)鍵是抽象;
2、兩種安全的實(shí)現(xiàn)開閉原則的設(shè)計(jì)模式是:Strategy pattern(策略模式),Template Methord(模版方法模式);
3、依據(jù)開閉原則,我們盡量不要修改類,只擴(kuò)展類,但在有些情況下會(huì)出現(xiàn)一些比較怪異的狀況,這時(shí)可以采用幾個(gè)類進(jìn)行組合來完成;
4、將可能發(fā)生變化的部分封裝成一個(gè)對(duì)象,如: 狀態(tài), 消息,,算法,數(shù)據(jù)結(jié)構(gòu)等等 , 封裝變化是實(shí)現(xiàn)"開閉原則"的一個(gè)重要手段,如經(jīng)常發(fā)生變化的狀態(tài)值,如溫度,氣壓,顏色,積分,排名等等,可以將這些作為獨(dú)立的屬性,如果參數(shù)之間有關(guān)系,有必要進(jìn)行抽象。對(duì)于行為,如果是基本不變的,則可以直接作為對(duì)象的方法,否則考慮抽象或者封裝這些行為;
5、在許多方面,OCP是面向?qū)ο笤O(shè)計(jì)的核心所在。遵循這個(gè)原則可帶來面向?qū)ο蠹夹g(shù)所聲稱的巨大好處(靈活性、可重用性以及可維護(hù)性)。然而,對(duì)于應(yīng)用程序的每個(gè)部分都肆意地進(jìn)行抽象并不是一個(gè)好主意。應(yīng)該僅僅對(duì)程序中呈現(xiàn)出頻繁變化的那部分作出抽象。拒絕不成熟的抽象和抽象本身一樣重要;