開放封閉原則(Open-Closed Principle,OCP):軟體中的對象(類,模塊,函數等等)應該對於擴展是開放的,但是對於修改是封閉的
---來源維基百科
簡單來說就是
盡可能達成不修改程式碼,然後擴張新功能。
這個想法我讀到的時候總覺得很有趣。
不過在開始之前,我們先來討論一下,
為什麼會有這種改是不可能改的想法出現。
最簡單的原因是因為,
軟體本身你只要不去動他,
它的穩定性就一定保持著那樣,
這跟現實其實是有一些違背的。
一件物體放在現實中,過一段時間沒去動他就會自然損壞。
而程式不是,只要你當下確認是穩定的,那麼在硬體沒出現問題的狀況下,不論放多久都是一樣穩定的。
基於此就延伸出一個狀況,
既然改了有可能變壞,
那反過來說我不改程式碼不就好了。
沒錯,就是那麼消極的作為,
但確實很有用。
擴展是開放的,
這一點就比較容易理解,
一個程式總會有一些新的功能,
剛好筆者有一個類似的程式例子,
就以筆者撰寫的這項服務來說,
GetServices利用供應商創建對應的服務。
/// <summary>
/// 取得供應商服務
/// </summary>
/// <param name="pLocal"></param>
/// <returns></returns>
private CarInfomationService GetServices(string pLocal)
{
CarInfomationService lHotelService = null;
EnumSupplierCode enumReturn = new EnumSupplierCode();
//字串比對Enum的Description,找到對應的枚舉
foreach (var item in Enum.GetValues(typeof(EnumSupplierCode)))
{
if (GetDescription(item) == pLocal.ToUpper())
{
enumReturn = (EnumSupplierCode)item;
break;
}
}
switch (enumReturn)
{
case EnumSupplierCode.VOLVO:
lHotelService = new VOLVOService();
break;
case EnumSupplierCode.AMC:
lHotelService = new AMCService();
break;
case EnumSupplierCode.Tesla:
lHotelService = new TeslaService();
break;
}
return lHotelService;
}
GetServices利用供應商創建對應的服務,
這非常直觀,但能明顯察覺有兩個可能的問題,
第一個問題是,
筆者未來要加入一個新的供應商,不就要這個地方要加,這樣不就違反了OCP。
第二個問題
這種類型的功能往往會服務範圍很廣,
改了之後,一定有很多很多地方都會有類似的問題要改。
這兩個問題的解答,
在一開始就提過,
就像是以前考試的題目般,
答案就藏在題目中。
既然改程式碼內會違反,
那麼不改程式碼不就好了。
聽起來很不可思議,
但這其實就是利用其他地方去改,
就是所謂的讀取外部資料,
很常見的如DB、config等等都是處理的方式,
以該項案例來說,
基於只有單純的判斷供應商,
所以筆者可能會建置在config,
並且如果有多個專案會考慮用成套件或其他形式供多個專案一同使用。
這樣就能達成程式碼中的封閉,
並且如果要新增的時候也可以直接改外部配置就行。
當然如果要完成這點,
我們要先違反OCP原則,
對GetServices這個程式進行修改才能達到。
另一種常見的保持OCP,
就是採取介面,
最主要是因為各個介面的實現互相獨立不干擾,
並且要擴展要只需要新建一個新的實現。
筆者這邊先暫時不放置範例,
如果有人有需要筆者在補充,
因為後續也會解說到,筆者稍微偷一下懶。