開放封閉原則 開放封閉原則在 Clean Architecture 一書中是這樣被敘述的
一個軟體製品應該對於擴充是開放的,但對於修改是封閉的。
一個簡單的擴充需求,對於軟體開發上是巨大的改變。這是軟體架構的失敗。一個好的軟體架構可以將修改的程式碼減少到最低程度。在理想情形下這個值是0 。
擴展系統功能比較好的方式應該是新增程式碼,並不是去修改既有的程式碼來擴充系統功能。因為修改一個行之有年的系統內部邏輯,可能會發生改 A 壞 B ,如果有自動化測試來驗證既有的程式碼可以在第一時間找出來。 但是在實務上有自動化測試專案真的是少之又少(ps.可能祖上積德才會遇到吧…),如果當初在設計系統的時候能避免的這種改 A 壞 B 的情形,那軟體系統將更容易修改需求及擴充功能。舉例來說,這是一個手機介面。
1 2 3 4 5 6 7 8 9 10 11 public interface IMobilePhone { void TurnOn () ; void TurnOff () ; void Call (string phoneNumber ) ; }
接下來,我們可以定義不同的手機,並實現IMobilePhone.cs介面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class IPhone14 : IMobilePhone { public void TurnOn () { Console.WriteLine("IPhone14 is turning on." ); } public void TurnOff () { Console.WriteLine("IPhone14 is turning off." ); } public void Call (string phoneNumber ) { Console.WriteLine("IPhone14 is calling " + phoneNumber); } }
宣告 IMobilePhoneFactory.cs
1 2 3 4 5 6 public interface IMobilePhoneFactory { IMobilePhone Create (string mobilePhone ) ; }
建立 MobilePhoneFactory.cs 實作 IMobilePhoneFactory.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MobilePhoneFactory : IMobilePhoneFactory { public IMobilePhone Create (string mobilePhone ) { var type = typeof (Program).Assembly .GetExportedTypes() .SingleOrDefault(t => t.Name.Equals(mobilePhone) && typeof (IMobilePhone).IsAssignableFrom(t)); if (type is null ) { throw new ArgumentException(nameof (mobilePhone)); } return (IMobilePhone)Activator.CreateInstance(type); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class GooglePixel7 : IMobilePhone { public void TurnOn () { Console.WriteLine("GooglePixel7 phone is turning on." ); } public void TurnOff () { Console.WriteLine("GooglePixel7 phone is turning off." ); } public void Call (string phoneNumber ) { Console.WriteLine("GooglePixel7 phone is calling " + phoneNumber); } }
現在,當我們使用不同型號手機,只需要使用IMobilePhone.cs介面,而不需要知道實際的手機型別。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var mobilePhoneTypes = new List<string >{ "IPhone14" }; foreach (var mobilePhoneType in mobilePhoneTypes){ var mobilePhoneFactory = new MobilePhoneFactory(); var mobilePhone = mobilePhoneFactory.Create(mobilePhoneType); mobilePhone.TurnOn(); mobilePhone.Call("1234567890" ); mobilePhone.TurnOff(); }
這樣做的好處在於,當我們需要添加新的手機型號時,只需要實現 IMobilePhone.cs 介面即可,不需要修改現有使用端程式碼,只需要建立設定檔將所有手機型號進行註冊動作即可。
總結 開放封閉原則的目標是實現系統的可擴展性和靈活性,使系統易於擴展而不會因為修改而對現有程式碼造成較大的影響。
在類別層次上,建議設計類別時要對擴展開放,對修改封閉。
在架構層次上,建議將系統組織成一系列相互獨立的元件,將這些元件安排到依賴階層中而實現。這樣可以避免高層級元件受到低層級元件變更的影響,同時使系統具有高度的模組化和可擴展性。
總結,適用於類別層次和架構層次的設計原則,皆是實現系統的可擴展性和靈活性,同時保證系統的穩定性和可靠性。