使用哪种模式进行日志记录?依赖注入或服务定位器?

时间:2010-04-21 11:48:08

标签: c# design-patterns dependency-injection service-locator

考虑这种情况。我有一些业务逻辑,现在需要写入日志。

interface ILogger
{
    void Log(string stuff);
}

interface IDependency
{
    string GetInfo();
}

class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(string input)
    {
        // Process input
        var info = _dependency.GetInfo();
        var intermediateResult = PerformInterestingStuff(input, info);

        if (intermediateResult== "SomethingWeNeedToLog")
        {
            // How do I get to the ILogger-interface?
        }

        var result = PerformSomethingElse(intermediateResult);

        return result;
    }
}

您如何获得ILogger界面?我看到两种主要的可能性;

  1. 在构造函数上使用依赖注入传递它。
  2. 通过单一服务定位器获取。
  3. 您更喜欢哪种方法?为什么?还是有更好的模式?

    更新 请注意,我不需要记录所有方法调用。我只想记录在我的方法中可能发生或可能不发生的一些(罕见)事件。

9 个答案:

答案 0 :(得分:15)

我个人将两者混合在一起。

以下是我的惯例:

  • 来自静态环境 - 服务地点
  • 来自实例上下文 - 依赖注入

我觉得这给了我可测试性的正确平衡。我发现对使用服务位置而不是使用DI的类设置测试有点困难,所以这就是服务位置最终成为例外而不是规则的原因。不过,我的使用是一致的,所以要记住我需要写什么类型的测试并不难。

有些人提出了DI倾向于使构造者混乱的担忧。我不认为这是一个问题,但如果你有这种感觉,有许多替代方案使用DI,但避免构造函数参数。以下是Ninject的DI方法列表: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

您会发现大多数Inversion of Control容器都具有与Ninject相同的功能。我选择展示Ninject,因为他们有最简洁的样本。

希望这很有帮助。

编辑:要明确,我使用Unity和Common Service Locator。我有一个用于DI的Unity容器的单例实例,我的IServiceLocator实现只是该单例Unity容器的包装器。这样我就不需要做任何类型映射两次或类似的事情了。

除了追踪之外,我也发现AOP没有特别的帮助。我更喜欢手动记录,只是为了清晰。我知道大多数AOP日志框架都能够兼顾,但我大多数时候都不需要前者(AOP的面包和黄油)。当然,这只是个人偏好。

答案 1 :(得分:7)

记录器显然是您的业务逻辑所依赖的服务,因此应该像对待IDependency一样将其视为依赖项。在构造函数中注入记录器。

注意:即使AOP被称为 注入日志记录的方式,我也不同意它是这种情况下的解决方案。 AOP非常适合执行跟踪,但永远不会成为业务逻辑的一部分。

答案 2 :(得分:5)

我的小经验法则:

  • 如果它在类库中,请使用构造函数注入或属性注入以及空对象模式。

  • 如果它在主应用程序中,请使用服务定位器(或单例)。

我发现使用log4net时这很适用。您不希望类库与可能不存在的东西联系,但在应用程序中,您知道记录器将会存在,并且像log4net这样的库主要基于服务位置模式。

我倾向于认为日志记录是足够静态的,它并不真正需要DI。我不太可能在应用程序中更改日志记录实现,特别是因为每个日志记录框架都非常灵活且易于扩展。在类库中,当您的库可能需要被已使用不同记录器的多个应用程序使用时,这一点更为重要。

当然是YMMV。 DI很棒,但这并不意味着一切都需要DI'ed。

答案 3 :(得分:4)

我们将所有Logging / Tracing切换为PostSharp(AOP框架)属性。要为方法创建日志记录,您只需要为其添加属性即可。

优点:

  • 轻松使用 AOP
  • 明确关注点分离
  • 发生在编译时 - > 对性能影响最小

查看this

答案 4 :(得分:3)

也许这将是一个小小的offtopic,但为什么我们需要注入记录器,当我们只需键入类的开头时:

Logger logger = LogManager.GetLogger("MyClassName");

记录器在开发期间以及之后的维护期间不会发生变化。现代记录器是高度可定制的,所以参数

  

如果我想用数据库替换文本记录器怎么办?

错过了。

我不会否定使用依赖注入,我只是对你的思想感到好奇。

答案 5 :(得分:2)

你可以派生另一种类型,例如LoggableBusinessObject在其构造函数中使用记录器。这意味着您只需将记录器传入将使用它的对象:

public class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)   
    {   
        _dependency = dependency;   
    }   

    public virtual string DoSomething(string input)   
    {   
        // Process input   
        var info = _dependency.GetInfo();   
        var result = PerformInterestingStuff(input, info);   
        return result;   
    }   
}

public class LoggableBusinessObject : MyBusinessObject
{
    private ILogger _logger;

    public LoggableBusinessObject(ILogger logger, IDependency dependency)
        : base(dependency)
    {
        _logger = logger;
    }

    public override string DoSomething(string input)
    {
        string result = base.DoSomething(input);
        if (result == "SomethingWeNeedToLog")
        {
             _logger.Log(result);
        }
    }
}

答案 6 :(得分:1)

我更喜欢Singleton Service。

依赖注入会使构造函数混乱。

如果你可以使用AOP,那将是最好的。

答案 7 :(得分:0)

DI在这里会很好用。另一件需要注意的事情是AOP

答案 8 :(得分:0)

我建议不要采用这些方法。最好使用面向方面的编程。记录是AOP的“hello world”。