天天看点

驯服烂代码_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

驯服烂代码

继续阅读