【公告】因應 .NET5 2020 改版,原預計 6/1 開放 ASP.NET MVC 全系列課程訂閱暫停

SOLID 設計原則 - 開放 / 封閉原則(OCP)

遵循「單一職掌原則(SRP)」設計獨立類別封裝特定功能,雖然可以有效的釐清功能程式碼的設計,但也面臨系統擴充的問題,特別是那些具有共同功能的擴充場合,透過遵循「開放-封閉原則(OCP)」,藉由衍生機制支援功能的擴充(open for extension),並避免現存功能程式碼的修正(closed for modification),可以解決相關的問題。

回到 SRP 原則中討論的範例SOLID. SRP,為不同類型的單位轉換功能提供獨立的類別設計,假設我們需要為進行轉換的數據進行轉換前的判斷,以避免小於0的數值進入轉換運算,想要完成功能的調整,必須同時修正兩個類別,除此之外還會造成重複程式碼的問題,比較好的作法抽象化實作內容,以介面或是抽象類別建立功能架構,進透過衍生類別進行功能實作。

在討論 SRP 的範例基礎上,以下持續擴充程式內容。

public interface IUnitConversion
{
    double Conversions(double d);
}
public class OZConversion : IUnitConversion
{
    public double Conversions(double o)
    {
        double c = 0.033814;
        double ml = o / c;
        return ml;
    }
}
public class TemperatureConversion : IUnitConversion
{
    public double Conversions(double c)
    {
        double f = c * 9 / 5 + 32;        
return f;
    }
}

在這個修改的版本當中,建立一組新的介面 IUnitConversion 並且於其中定義轉換方法Conversions 的參數規格,而類別 OZConversion 與 TemperatureConversion 則實作介面Conversions 方法,配置所需的功能程式碼。

現在於專案中建立以下的方法成員 UnitConversion:

double UnitConversion(IUnitConversion conversion, double d)
{
    double result = conversion.Conversions(d);
    return result;
}

這個方法接受 IUnitConversion 物件,與要轉換的數值,方法中引用 Conversions 方法完成轉換作業並且回傳,現在無論是要執行盎司單位轉換或是溫度轉換,都可以在執行期間,透過這個方法傳入對應的物件實踐轉換功能,以下建立兩個方法支援相關的轉換:

public void Ozml()
{
    OZConversion conversion = new OZConversion();
    double oz = 10;
    double ml = UnitConversion(conversion, oz);
    ViewBag.ml = ml;
}
public void Ftoc()
{
    TemperatureConversion conversion = new TemperatureConversion();
    double c = 100;
    double f = UnitConversion(conversion, c);
    ViewBag.f = f;
}

Ozml 動作方法建立 OZConversion 物件,因此 UnitConversion 引用的會是盎司單位轉換的版本,而 Ftoc 動作方法引用的則是溫度轉換的版本。

OCP 的核心精神在於要求元件設計必須允許未來的擴充,稱為「open for extension」,但是不允許因此而修改現存的類別,也就是所謂的「closed for modification」,遵往這個原則,當我們想要進一步擴充單位轉換功能,只需加入實作 IUnitConversion 介面的新類別,不需要調整目前的任何類別實作即可達到擴充的目的,當然也不需要修正控制器中的轉換函式 UnitConversion 。


沒有留言: