依赖注入+环境上下文+服务定位器

时间:2012-05-21 10:01:44

标签: dependency-injection service-locator cross-cutting-concerns

最近我读了很多关于应用程序设计模式的东西:关于DI,SL反模式,AOP等等。原因 - 我想要达成设计妥协:松散耦合,干净且易于使用。除了一个问题外,DI似乎几乎就像一个解决方案:交叉和可选的依赖导致构造函数或属性污染。所以我为此提出了自己的解决方案,我想知道你怎么看

Mark Seemann(DI书的作者和着名的“SL is anti patter”声明)在他的书中提到了一种叫做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为null。这就是我的代码要做的事情:在未配置SL时使用默认设置。

2 个答案:

答案 0 :(得分:3)

您提出的环境上下文问题在于它使测试更加困难。原因有两个:

  1. 在运行单元测试时,必须始终在“ServiceLocator.Current”中注册有效实例。但不仅如此,它必须使用有效的ILogger注册。
  2. 当你需要在测试中使用假记录器时(除了简单的 NullLogger),你必须配置你的容器 没有办法挂钩,但由于容器是单件,所有其他测试都将使用相同的记录器。
  3. 创建一个在单元测试并行运行时可行的解决方案(并且默认情况下是MSTest),这将是非常重要的(并且浪费时间)。
  4. 所有这些问题都可以通过简单地将ILogger实例注入需要它的服务来解决,而不是使用环境上下文。

    如果您系统中的许多课程依赖于ILogger抽象,那么您应该认真地问自己whether you're logging too much

    另请注意dependencies should hardly ever be optional

答案 1 :(得分:3)

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

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