天天看點

馴服爛代碼_C#中的馴服配置

馴服爛代碼

馴服爛代碼_C#中的馴服配置

問題

我們有很多C#代碼想要通路某種配置,而所有這些都是通過以下方式完成的:

ConfigurationManager.AppSettings["SomeSettingOrOther"]

例如,我們有一個

CurrencyConversion

類,它具有以下代碼。

public class CurrencyConversion { Currency GetDefaultCurrency() { // get the config setting string configCurrency = ConfigurationManager.AppSettings["MarketCurrency"];

// return the equivalent Enum return CurrencyFromString(configCurrency); } }

此代碼有幾個問題。

  • 它對

    MarketCurrency

    設定的依賴是隐式的(即,我們不能通過檢視類的公共接口來了解這一點,我們必須檢視内部)。
  • 如果配置設定丢失或格式不正确,它可能會引發異常,并且隻有在運作此特定代碼段時,我們才會發現這一點。
  • 代碼的其他部分可能也使用此配置設定,這将重複字元串到Enum的轉換以及任何錯誤處理。
  • 可能是因為使用了錯誤的配置設定(或為了友善起見,重用了現有設定),并且應該更正确地使用

    DefaultCurrency

    。 通過檢視代碼我們無法知道這一點,而這些知識僅存在于開發人員的頭腦中。
  • 測試很困難,因為我們必須設定ConfigurationManager,并且在确定所需的配置時可能會遇到一些反複試驗。
  • 配置源(

    ConfigurationManager

    )是寫死的,并且可能在整個代碼庫中有所不同(即某些使用ConfigurationManager的方法和某些讀取環境變量的方法)

還有更高層次的問題。 這些調用散布在整個代碼中,有時深入共享庫中,并且無法知道哪些代碼位需要哪些設定(不讀取所有代碼)。

這意味着很難驗證配置檔案是否包含了所需的所有資訊,而且沒有人敢于删除配置設定。 反過來,這導緻我們的配置檔案變得腫且令人困惑。

解決方案

為了解決這些問題,我們轉向使用接口來定義我們的配置。

如果我們重構上面的示例代碼以通過接口進行配置,則會得到以下資訊(在現實生活中,由于隻有一種設定,您可能決定直接将其直接傳遞,但請耐心等待)。

public interface ICurrencyConversionConfiguration { Currency DefaultCurrency; }

public class CurrencyConversion { readonly ICurrencyConversionConfiguration configuration;

public CurrencyConversion(ICurrencyConversionConfiguration configuration) { Contract.Requires(configuration != null);

this.configuration = configuration; }

Currency GetDefaultCurrency() { return configuration.DefaultCurrency; } }

該代碼具有以下改進

  • 現在,它對

    DefaultCurrency

    依賴性是明确的。 沒有它就無法建立該類。
  • 通過構造函數注入可以滿足對

    DefaultCurrency

    的依賴關系。
  • 測試時很容易模拟

    ICurrencyConversionConfiguration

  • 名稱是一緻的。
  • 讀取和解析配置的責任已被删除。

為了處理讀取和解析配置的責任,我們在下面添加了代碼。 這依賴于一個簡單的

Configuration

類,您可以在GitHub上的https://github.com/resgroup/configuration上看到它。

public class EconomicModelConfiguration : ICurrencyConversionConfiguration { readonly Configuration configuration; public EconomicModelConfiguration(Configuration configuration) { Contract.Requires(configuration != null);

this.configuration = configuration; Validate(); } void Validate() => using (var validator = configuration.CreateValidator) validator.Check(() => DefaultCurrency);

public string DefaultCurrency => configuration.GetEnum<Currency>(MethodBase.GetCurrentMethod()); }

配置類本身由配置源執行個體化,該源從下面的示例中的環境變量中讀取。 這樣可以輕松遵守12項因子應用程式的建議( https://12factor.net/config )。

new Configuration(new GetFromEnvironment());

這具有以下改進

  • 建立類時将檢查配置設定(應在應用程式的入口點),是以會立即清除所有配置問題。
  • 從字元串轉換為貨币的代碼是集中的。
  • 配置源已封裝。

如果我們移動另一個類以使用新的配置系統,則會得到類似的資訊。

public class EconomicModelConfiguration : ICurrencyConversionConfiguration, IConcreteCostConfiguration { readonly Configuration configuration; public EconomicModelConfiguration(Configuration configuration) { Contract.Requires(configuration != null);

this.configuration = configuration; Validate(); } void Validate() { using (var validator = configuration.CreateValidator) { validator.Check(() => DefaultCurrency); validator.Check(() => DefaultConcreteCost); } }

public string DefaultCurrency => configuration.GetEnum<Currency>(MethodBase.GetCurrentMethod());

public double DefaultConcreteCost => configuration.GetDouble(MethodBase.GetCurrentMethod()); }

随着将更多類移至新系統,該過程将繼續進行,并具有易于安裝控制反轉的優勢,因為我們隻需向其實作的所有接口注冊

EconomicModelConfiguration

即可。

舊版代碼

像任何成熟的軟體團隊一樣,我們有一些遺留代碼,其中一些尚無法通過控制反轉建立。

對于這些類,我們建立一個靜态Configuation類

public static class EconomicModelConfigurationStatic { readonly static EconomicModelConfiguration base = new EconomicModelConfiguration();

public static IEconomicModelConfiguration Settings => base; }

然後在舊版代碼中,替換

ConfigurationManager.AppSettings["SomeSettingOrOther"]

EconomicModelConfigurationStatic.Settings.SomeSettingOrOther

這給我們帶來了新系統的很多好處,隻需對現有代碼進行很小的改動即可。

結論

以這種方式封裝配置邏輯并通過接口提供配置具有以下好處。

  • 沒有魔術字元串 ,是以在編譯時會捕獲任何拼寫錯誤
  • 可以使用重構工具 (例如

    rename

    ),并保證所有執行個體都已更新
  • 使用Visual Studio可以輕松找到對配置項的所有引用
  • 代碼是明确的有關所需配置的資訊,并且隻能定義所需配置的子集
  • 可以檢查配置檔案以檢視它們是否包含所有必需的資訊
  • 可以檢查配置檔案以檢視它們是否包含任何多餘資訊
  • 配置邏輯(例如預設值和轉換)集中處理
  • 保證配置項在配置檔案和代碼中具有相同的名稱

如果您想使用它,可以使用nuget包 ,其源位于GitHub上 。

翻譯自: https://hackernoon.com/taming-configuration-in-c-a2706b2d4741

馴服爛代碼

繼續閱讀