2

最近我读了很多关于应用程序设计模式的东西:关于 D​​I、SL 反模式、AOP 等等。原因——我想在设计上做出妥协:松散耦合、干净且易于使用。DI 似乎几乎是一种解决方案,除了一个问题:横切和可选依赖导致构造函数或属性污染。因此,我为此提出了自己的解决方案,我想知道您对此有何看法。

Mark Seemann(DI 书籍的作者和著名的“SL 是反模式”声明的作者)在他的书中提到了一种称为 Ambient Context 的模式。尽管他说他不太喜欢它,但这种模式仍然很有趣:它就像旧的好单例,只是它是作用域的并提供默认值,因此我们不必检查 null。它有一个缺陷——它没有而且它不知道它的范围以及如何处置自己。

那么,为什么不在这里应用服务定位器呢?它可以解决环境上下文对象的作用域和处置问题。在你说它是反模式之前:这是你隐藏合同的时候。但在我们的例子中,我们隐藏了 OPTIONAL 合同,所以 IMO 并没有那么糟糕。

这里有一些代码来说明我的意思:

public interface ILogger
{
    void Log(String text);
}

public interface ISomeRepository
{
    // skipped
}


public class NullLogger : ILogger
{
    #region ILogger Members

    public void Log(string text)
    {
        // do nothing
    }

    #endregion
}

public class LoggerContext
{
    public static ILogger Current
    {
        get
        {
            if(ServiceLocator.Current == null)
            {
                return new NullLogger();
            }
            var instance = ServiceLocator.Current.GetInstance<ILogger>();
            if (instance == null)
            {
                instance = new NullLogger();
            }
            return instance;
        }
    }
}

public class SomeService(ISomeRepository repository)
{
    public void DoSomething()
    {
        LoggerContext.Current.Log("Log something");
    }
}

编辑:我意识到不问具体问题与堆栈溢出设计相冲突。因此,我会将一篇最好地描述为什么这种设计不好或更好地提供更好的解决方案(或者可能是添加?)的帖子标记为答案。但是不要建议 AOP,它很好,但是当你真的想在你的代码中做一些事情时,它不是一个解决方案。

编辑 2:我添加了对 ServiceLocator.Current 的检查是否为空。这就是我的代码要做的事情:在未配置 SL 时使用默认设置。

4

2 回答 2

3

您可以使用手工制作的装饰器或某种拦截(例如Castle DynamicProxyUnity 的拦截扩展)添加横切关注点。

因此,您根本不必注入ILogger您的核心业务类。

于 2012-05-21T10:48:38.093 回答
3

您提出的环境上下文的一个问题是它使测试变得更加困难。有几个原因:

  1. 在运行单元测试时,必须始终在“ServiceLocator.Current”中注册一个有效的实例。但不仅如此,它还必须使用有效的ILogger.
  2. 当您需要在测试中使用假记录器(除了简单的 NullLogger)时,您将必须配置您的容器,因为没有办法挂钩,但由于容器是单例,所有其他测试都将使用同一个记录器。
  3. 当您的单元测试并行运行时(正如 MSTest 默认情况下所做的那样),创建一个可以工作的解决方案将是非常重要的(并且是浪费时间)。

所有这些问题都可以通过简单地将ILogger实例注入到需要它的服务中来解决,而不是使用环境上下文。

如果您系统中的许多类都依赖于该ILogger抽象,那么您应该认真问问自己是否记录了太多

另请注意,依赖项几乎不应该是可选的

于 2012-05-21T10:38:00.267 回答