5

我正在开发一个简化应用程序配置的库。本质上,库的使用者要么用属性装饰他们的配置类,要么在代码中以声明方式初始化设置。可以指定 1 个或多个源来读取/写入配置属性(访问器)或从类继承默认值。例如,以下内容:

[ConfigurationNamespace(DefaultAccessors = new Type[] { typeof(AppSettingsAccessor) })]
public class ClientConfiguration : Configuration<IClientConfiguration>
{
    [ConfigurationItem(Accessors = new Type[] { 
        typeof((RegistryAccessor)) 
     })]
    public bool BypassCertificateVerification { get; set; }
}

相当于

var config = new Configuration<IClientConfiguration>();
config.SetDefaultAccessors(new[] { typeof(AppSettingsAccessor) });
config.SetPropertyAccessors(
    property: x => x.BypassCertificateVerification,
    accessors: new[] { typeof(RegistryAccessor) }
);

访问器处理来自各种来源(AppSettings、注册表、.ini 等)的读取和写入。我想让消费者扩展功能以满足他们的需求。我想让它与 IoC 容器无关。之所以给我 Type[] 约束,是因为由于编译时与运行时问题,我无法在属性中指定类型。

有没有办法有一个默认机制来实例化这些(例如,基于 Activator.CreateInstance 的东西),但也允许消费代码在运行时实例化这些访问器而不使用服务定位器/依赖解析器模式?我一直在阅读很多关于为什么服务定位器/依赖解析器模式是一种邪恶的反模式的文章,但我想不出一个更好的工具来完成这项工作。我看到 MVC 框架和 SignalR 库使用依赖解析器。他们是 100% 的邪恶,还是这是一个极端情况?据我所知,抽象工厂模式不会削减它,因为它不喜欢类型参数。

在这种特殊情况下,基于属性的配置将比声明性方法更有用,因此我不想放弃我的配置属性(这将允许我将 Type 更改为 IConfigurationAccessor 并切换到工厂方法)。

4

1 回答 1

5

从 DSL 的工作中,我们知道将 API与其他表达模式分离为底层语义模型非常重要。在这种情况下,Configuration<T>API 在我看来就像是语义模型。没有理由在基于属性的 DSL 之后对语义模型进行建模。这样的事情对我来说更有意义:

var config = new Configuration<IClientConfiguration>();
config.DefaultAccessors.Add(new AppSettingsAccessor());
config.PropertyAccessorsFor(x => x.BypassCertificateVerification)
    .Add(new RegistryAccessor());

我还将基于属性的模型更改为纯声明性的:

[AppSettingsConfiguration]
public class ClientConfiguration : Configuration<IClientConfiguration>
{
    [RegistryConfiguration]
    public bool BypassCertificateVerification { get; set; }
}

现在您“只”需要找出一种从自适应模型转换为语义模型的方法。

这基本上就像任何其他序列化阅读器一样:读取数据,并从类型注释中获取有关如何读取数据的提示。这基本上是对数据结构的递归遍历,并且您需要为每个节点创建一个访问器。

假设所有访问器都实现了类似的接口IAccessor,则可以使用抽象工厂进行扩展:

public interface IAccessorFactory
{
    IAccessor CreateAccessor(ConfigurationAttribute configurationAttribute);
}

实际上,这更具体地说是PLoPD4中描述的称为Product Trader的一种不太广为人知的设计模式,但由于大多数人不知道这种模式,我们就称它为抽象工厂——它不是服务定位器,因为它不返回一个无界类型——它只返回 IAccessor 实例。

于 2013-02-22T16:02:37.927 回答