## 一. 定義
開閉原則(Open-Closed Principle, OCP)是指一個軟件實體如類、模塊和函數應該**對擴展開放,對修改關閉**。
**強調的是用抽象構建框架,用實現擴展細節。**
開閉原則,是面向對象設計中最基礎的設計原則。它指導我們如何建立穩定靈活的系統。
例如:我們版本更新,盡可能不修改原代碼,但是可以增加新功能。
## 二. 優點
##### ??**1.保持軟件產品的穩定性**
???開閉原則要求我們通過保持原有代碼不變,添加新代碼來實現軟件的變化,因為不涉及原代碼的改動,這樣可以避免為實現新功能而改壞線上功能的情況,避免老用戶的流失。
##### ??**2.不影響原有測試代碼的運行**
???軟件開發規范性好的團隊都會寫單元測試,如果原有的某個功能發生了變化,則單元測試代碼也應做相應的變更,否則就有可能導致測試出錯。如果每次軟件的變化,除了變更功能代碼之外,還得變更測試代碼,書寫測試代碼同樣需要消耗工時,這樣在項目中引入單元測試就成了累贅。開閉原則可以讓單元測試充分發揮作用而又不會成為后期軟件開發的累贅。
##### ??**3.使代碼更具模塊化,易于維護**
???開閉原則可以讓代碼中的各功能,以及新舊功能獨立存在于不同的單元模塊中,一旦某個功能出現問題,可以很快地鎖定代碼位置作出修改,由于模塊間代碼獨立不相互調用,更改一個功能的代碼也不會引起其他功能的崩潰。
##### ??**4.提高開發效率**
???在項目開發過程中,有時候閱讀前人的代碼是件很頭疼的事,尤其項目開發周期比較長,可能三五年,再加上公司人員流動性大,原有代碼的開發人員早就另謀高就,而代碼寫的更是一團糟,自帶混淆,能走彎路不走直路。而現在需要在原有功能的基礎上開發新功能,如果開閉原則使用得當的話,我們是不需要看懂原有代碼實現細節便可以添加新代碼實現新功能,畢竟有時候閱讀一個功能的代碼,比自己重新實現這個功能用的時間還要長。
## 三、示例
`假設我們使用出售電腦為例,首選定義一個頂層接口Computer:`
~~~
/**
* 頂層接口,定義了獲取電腦信息的接口方法
*/
public interface Computer {
double getPrice();//價格
String getColor();//顏色
int getMemory();//內存
float getSize();//尺寸
}
~~~
`然后定義兩個實現類,華碩電腦與蘋果Mac`
~~~
/**
* 華碩
*/
public class AsusComputer implements Computer {
private double price;
private String color;
private int memory;
private float size;
public AsusComputer(double price, String color, int memory, float size) {
this.price = price;
this.color = color;
this.memory = memory;
this.size = size;
}
@Override
public double getPrice() { return this.price; }
@Override
public String getColor() { return this.color; }
@Override
public int getMemory() { return this.memory; }
@Override
public float getSize() { return this.size; }
}
~~~
~~~
/**
* Mac
*/
public class MacComputer implements Computer{
private double price;
private String color;
private int memory;
private float size;
public MacComputer(double price, String color, int memory, float size) {
this.price = price;
this.color = color;
this.memory = memory;
this.size = size;
}
@Override
public double getPrice() { return this.price; }
@Override
public String getColor() { return this.color; }
@Override
public int getMemory() { return this.memory; }
@Override
public float getSize() { return this.size; }
}
~~~
??測試類:
~~~
public class Test {
public static void main(String\[\] args) {
Computer computer = new AsusComputer(4888.88D,"深藍",8,14.0F);
System.out.println(
"電腦:華碩\n" +
"售價:" + computer.getPrice() + "\n" +
"顏色:" + computer.getColor() + "\n" +
"內存:" + computer.getMemory() + "\n" +
"尺寸:" + computer.getSize()
);
}
}
~~~
~~~
電腦:華碩
售價:4888.88
顏色:深藍
內存:8
尺寸:14.0
~~~
這是我們一開始的需求,但是隨著軟件發布運行,我們需求不可能一成不變,肯定要接軌市場。
假設現在是雙十一,需要搞促銷活動。那么我們的代碼肯定要添加新的功能。
可能有些剛入職的新人會在原有的代碼上做改動:
~~~
@Override
public double getPrice() {
return this.price * 0.6;
}
~~~
這肯定不符合我們的開閉原則,雖然看起來這樣做最直接,也最簡單,但是絕大部分項目中,
一個功能的實現遠比想像要復雜的多,我們在原有的代碼中進行修改,其風險遠比擴展和實現一個方法要大的多。
正確的做法可以這樣:
~~~
/**
* 華碩電腦打折
*/
public class AsusDiscountComputer extends AsusComputer {
private float discount;
public AsusDiscountComputer(double price, String color, int memory, float size, float discount) {
super(price, color, memory, size);
this.discount = discount;
}
public double getDiscountPrice(){
return getPrice() * this.discount;
}
}
~~~
實現一個關于折扣的子類,其中包含一個關于折扣的方法,這方法相當于一個擴展方法。可以看到這個子類是AsusComputer的,那為什么不把他設計成一個共用的折扣類呢,比如DiscountComputer,所有實現類都繼承這個
折扣類。這是因為每種實現類的折扣方案可能是不一樣的。所以我們最好能把它作為每個實現類的子類單獨實現。
如果你能確保你的業務中的新功能能兼容所有相關聯的需求你也可以共用一個。
~~~
public class Test1 {
public static void main(String[] args) {
Computer computer = new AsusDiscountComputer(4888.88D,"深藍",8,14.0F,0.5F);
AsusDiscountComputer asusDiscountComputer = (AsusDiscountComputer)computer;
System.out.println(
"電腦:華碩\n" +
"原價:" + asusDiscountComputer.getPrice() + "\n" +
"售價:" + asusDiscountComputer.getDiscountPrice() + "\n" +
"顏色:" + asusDiscountComputer.getColor() + "\n" +
"內存:" + asusDiscountComputer.getMemory() + "\n" +
"尺寸:" + asusDiscountComputer.getSize()
);
}
}
~~~
~~~
電腦:華碩
原價:4888.88
售價:2444.44
顏色:深藍
內存:8
尺寸:14.0
~~~
你可以看到如果想要調用getDiscountPrice()方法,在原有的基礎上你還要對它進行強轉,如果
能確定新擴展的需求,能兼容原有的繼承體系,你也可以把它抽取到頂層的Computer的接口中。
最后看一下繼承體系

- 前言
- 第一章 設計七大原則
- 第1節 開閉原則
- 第2節 依賴倒置原則
- 第3節 單一職責原則
- 第4節 接口隔離原則
- 第5節 迪米特法則
- 第6節 里氏替換原則
- 第7節 合成復用原則
- 第二章 簡單工廠模式
- 第1節 使用場景
- 第2節 示例代碼
- 第三章 創建者模式
- 第1節 工廠方法模式
- 第2節 抽象工廠模式
- 第3節 建造者模式
- 第4節 原型模式
- 第5節 單例模式
- 第四章 結構型模式
- 第1節 適配器模式
- 第2節 橋接模式
- 第3節 組合模式
- 第4節 裝飾者模式
- 第5節 外觀模式
- 第6節 享元模式
- 第7節 代理模式
- 第五章 行為模式
- 第1節 責任鏈模式
- 第2節 命令模式
- 第3節 迭代器模式
- 第4節 中介者模式
- 第5節 備忘錄模式
- 第6節 觀察者模式
- 第7節 狀態模式
- 第8節 策略模式
- 第9節 模板方法模式
- 第10節 訪問者模式
- 第11節 解釋器模式