C#、Unity IoC:注册和解析通用接口,良好实践
C#, Unity IoC: Registering and resolving generic interfaces, good practices
我编写了通用接口,随着时间的推移,我开始经常使用它。我还想提供另一个通用接口,扩展现有接口。
现在,当我查看我的 Unity 配置(此处与 IoC 框架无关)时,它看起来像这样:
container.RegisterType<IConfigProvider<ICountryConfig>, CountryConfigProvider>();
container.RegisterType<IConfigProvider<ILanguageConfig>, LanguageConfigProvider>();
// IEnumerableConfigProvider<T> : IConfigProvider<IEnumerable<T>>
container.RegisterType<IEnumerableConfigProvider<ILocaleConfig>, LocaleConfigProvider>();
// ... repeat gazillion of times...
作为依赖使用:
public LocaleResolver(IConfigProvider<ICountryConfig> countryConfigProvider, ...)
{
// similar construct all over the place
}
其中 ICountryConfig
、ILanguageConfig
、... 是描述数据库实体的接口。提供程序然后注意向依赖组件提供正确的实体,以便对存储的访问保留在自己的位置。
当我几乎没有重构时出现了问题。我不小心使用了父接口而不是继承的接口。当使用错误的接口类型时,它也可能非常脆弱。
这让我产生了(显而易见的)想法,即创建另一层接口来封装具体用法。这将解决上述问题,另一方面,我最终会为每个可能的实现提供万亿个空接口。
// ICountryConfigProvider : IConfigProvider<ICountryConfig>
container.RegisterType<ICountryConfigProvider, CountryConfigProvider>();
// ILanguageConfigProvider : IConfigProvider<ILanguageConfig>
container.RegisterType<ILanguageConfigProvider, LanguageConfigProvider>();
// ILocaleConfigProvider : IEnumerableConfigProvider<ILocaleConfig>
container.RegisterType<ILocaleConfigProvider, LocaleConfigProvider>();
它的用法更简洁:
public LocaleResolver(ICountryConfigProvider countryConfigProvider, ...)
{
// ...
}
而我现在正在寻找的是一个很好的建议。也许我在这里过度滥用 IoC。中型项目的最佳方法是什么?
我知道这个话题可能会以讨论结束,而不是具体的答案,我为亲爱的 Whosebug 道歉。
如果每个提供者都是不同的具体类型,那么这种方法虽然带有空接口,但很冗长,但也是有效的。另一个不那么冗长的解决方案是概括配置访问,因此您只有一个配置 'service' returns 许多特定配置 blob 中的任何一个。
Eg configProvider.GetConfig<IWhateverConfig>();
这样一来,您就不会在容器中塞满大量提供程序,本质上只是检索某种类型的 属性 装满配置的包。您将注册一个配置提供程序,并在幕后将配置类型映射到配置部分。如果您公开此映射功能,则可以在应用程序启动期间的任何时候以更模块化的方式注册配置 blob。
我编写了通用接口,随着时间的推移,我开始经常使用它。我还想提供另一个通用接口,扩展现有接口。
现在,当我查看我的 Unity 配置(此处与 IoC 框架无关)时,它看起来像这样:
container.RegisterType<IConfigProvider<ICountryConfig>, CountryConfigProvider>();
container.RegisterType<IConfigProvider<ILanguageConfig>, LanguageConfigProvider>();
// IEnumerableConfigProvider<T> : IConfigProvider<IEnumerable<T>>
container.RegisterType<IEnumerableConfigProvider<ILocaleConfig>, LocaleConfigProvider>();
// ... repeat gazillion of times...
作为依赖使用:
public LocaleResolver(IConfigProvider<ICountryConfig> countryConfigProvider, ...)
{
// similar construct all over the place
}
其中 ICountryConfig
、ILanguageConfig
、... 是描述数据库实体的接口。提供程序然后注意向依赖组件提供正确的实体,以便对存储的访问保留在自己的位置。
当我几乎没有重构时出现了问题。我不小心使用了父接口而不是继承的接口。当使用错误的接口类型时,它也可能非常脆弱。
这让我产生了(显而易见的)想法,即创建另一层接口来封装具体用法。这将解决上述问题,另一方面,我最终会为每个可能的实现提供万亿个空接口。
// ICountryConfigProvider : IConfigProvider<ICountryConfig>
container.RegisterType<ICountryConfigProvider, CountryConfigProvider>();
// ILanguageConfigProvider : IConfigProvider<ILanguageConfig>
container.RegisterType<ILanguageConfigProvider, LanguageConfigProvider>();
// ILocaleConfigProvider : IEnumerableConfigProvider<ILocaleConfig>
container.RegisterType<ILocaleConfigProvider, LocaleConfigProvider>();
它的用法更简洁:
public LocaleResolver(ICountryConfigProvider countryConfigProvider, ...)
{
// ...
}
而我现在正在寻找的是一个很好的建议。也许我在这里过度滥用 IoC。中型项目的最佳方法是什么?
我知道这个话题可能会以讨论结束,而不是具体的答案,我为亲爱的 Whosebug 道歉。
如果每个提供者都是不同的具体类型,那么这种方法虽然带有空接口,但很冗长,但也是有效的。另一个不那么冗长的解决方案是概括配置访问,因此您只有一个配置 'service' returns 许多特定配置 blob 中的任何一个。
Eg configProvider.GetConfig<IWhateverConfig>();
这样一来,您就不会在容器中塞满大量提供程序,本质上只是检索某种类型的 属性 装满配置的包。您将注册一个配置提供程序,并在幕后将配置类型映射到配置部分。如果您公开此映射功能,则可以在应用程序启动期间的任何时候以更模块化的方式注册配置 blob。