開(kāi)閉原則(Open-Close Principle,OCP):
定義:規(guī)定軟件中的對(duì)象、類(lèi)、模塊和函數(shù)對(duì)擴(kuò)展應(yīng)該是開(kāi)放的,但對(duì)于修改是封閉的。
這意味著應(yīng)該用抽象定義結(jié)構(gòu),用具體實(shí)現(xiàn)擴(kuò)展細(xì)節(jié),以此確保軟件系統(tǒng)開(kāi)發(fā)和維護(hù)過(guò)程的可靠性。
對(duì)于外部的調(diào)用方來(lái)說(shuō),只要能體現(xiàn)出面向抽象編程,定義出接口并實(shí)現(xiàn)其方法,即不修改原有方法體,只通過(guò)繼承方式進(jìn)行擴(kuò)展,都可以體現(xiàn)出開(kāi)閉原則。
聽(tīng)起來(lái)很簡(jiǎn)單是不?
我們來(lái)更好的理解一下:一個(gè)軟件實(shí)體如類(lèi),模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放(對(duì)提供方),對(duì)修改關(guān)閉(對(duì)使用方)。用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。
也就是說(shuō)如果出現(xiàn)使用方需要修改原有代碼來(lái)實(shí)現(xiàn)變化的情況,就等于違反了開(kāi)閉原則。
通俗解釋就是,添加一個(gè)新的功能,應(yīng)該通過(guò)在已有代碼(模塊、類(lèi)、方法)的基礎(chǔ)上進(jìn)行擴(kuò)展來(lái)實(shí)現(xiàn),而不是修改已有代碼
那這就要求我們,對(duì)于業(yè)務(wù)系統(tǒng),要想識(shí)別出盡可能多的擴(kuò)展點(diǎn),就要求你對(duì)業(yè)務(wù)有足夠的了解,能夠預(yù)見(jiàn)一些未來(lái)可能的變化。
對(duì)于偏技術(shù)的系統(tǒng),比如,框架、組件、類(lèi)庫(kù)等,就需要充分了解它的使用場(chǎng)景?以及今后想要擴(kuò)展點(diǎn)功能?使用者(業(yè)務(wù)程序員)未來(lái)會(huì)有哪些更多的訴求.
舉例:
用計(jì)算三種形狀的面積的例子,長(zhǎng)方形、三角形、圓形,它們?cè)陬?lèi)中已經(jīng)按照固定的公式實(shí)現(xiàn),圓形面積公式中π=3.14。但后續(xù)由于π值取的精度對(duì)于某些場(chǎng)景是不足的,需要擴(kuò)展,接下來(lái)就通過(guò)模擬這個(gè)場(chǎng)景體現(xiàn)開(kāi)閉原則。
public interface ICalculationArea {
/**長(zhǎng)方形面積計(jì)算*/
double rectangle(double x, double y);
/**三角形面積計(jì)算*/
double triangle(double x, double y, double z);
/**圓形面積計(jì)算*/
double circular(double r);
}
長(zhǎng)方形面積,長(zhǎng)×寬。
三角形面積,使用海倫公式,S=

,其中p=(a+b+c)/2。
圓形面積:

實(shí)現(xiàn)類(lèi):
public class CalculationArea implements ICalculationArea{
private final static double π = 3.14D;
@Override public double rectangle(double x, double y) {
return x * y;
}
@Override public double triangle(double x, double y, double z) {
double p = (x + y + z) / 2;
return Math.sqrt(p * (p - x) *(p - y) * (p - z));
}
@Override public double circular(double r) {
return π * r * r;
}
}
其中圓形面積的 π 值取的是 3.14D. 現(xiàn)在我們需要擴(kuò)展circular方法,讓其支持精度更高的場(chǎng)景
違背原則方案, 直接將CalculationArea中 π 的值改掉:
private final static double π = 3.141592653D;
@Override public double circular(double r) {
return π * r * r;
}
開(kāi)閉原則方案:
繼承父類(lèi)擴(kuò)展需要的方法,同時(shí)可以保留原有的方法,新增自己需要的方法。
public class CalculationAreaExt extends CalculationArea{
private final static double π = 3.141592653D;
@Override
public double circular(double r) {
return π * r * r;
}
}
擴(kuò)展后的方法已經(jīng)把求圓形面積的精度增長(zhǎng),需要使用此方法的用戶(hù)可以直接調(diào)用。而其他的方法,如長(zhǎng)方形面積、三角形面積,則可以繼續(xù)使用。
開(kāi)閉原則中對(duì)于修改是封閉的并非是一個(gè)絕對(duì)的概念。
1.修復(fù)缺陷所做的改動(dòng)
缺陷在軟件中很常見(jiàn),是不可能完全消除的。當(dāng)缺陷出現(xiàn)時(shí),就需要我們修復(fù)現(xiàn)有的代碼。軟件修復(fù)明顯傾向于實(shí)用主義而不是堅(jiān)持開(kāi)放封閉原則。
2.客戶(hù)端無(wú)法感知到的改動(dòng)
如果一個(gè)類(lèi)的改動(dòng)會(huì)引起另一個(gè)類(lèi)的改動(dòng),那么這兩個(gè)類(lèi)就是緊密耦合的。相反,如果一個(gè)類(lèi)的修改總是獨(dú)立的,并不會(huì)引起其他類(lèi)的改動(dòng),那么這些類(lèi)就是松散耦合的。我們要記住,任何情況下,松散耦合都比緊密耦合要好。如果我們對(duì)現(xiàn)有代碼的修改不會(huì)影響客戶(hù)端代碼,那么也就談不上違背開(kāi)放封閉原則。
3.修改還是擴(kuò)展?
從開(kāi)閉原則定義中,我們可以看出,開(kāi)閉原則可以應(yīng)用在不同粒度的代碼中,可以是模塊,也可以類(lèi),還可以是方法(及其屬性)。同樣一個(gè)代碼改動(dòng),在粗代碼粒度下,可以被認(rèn)定為“修改”,但在細(xì)代碼粒度下,又可以被認(rèn)定為“擴(kuò)展”。
比如,在類(lèi)這個(gè)層面添加屬性和方法相當(dāng)于修改類(lèi),這個(gè)代碼改動(dòng)可以被認(rèn)定為“修改”;但這個(gè)改動(dòng)并沒(méi)有修改已有的屬性和方法,在方法(及其屬性)這一層面,它又可以被認(rèn)定為“擴(kuò)展”。