带有Singleton Logger类的DI / IoC

时间:2012-08-21 19:15:06

标签: c# .net design-patterns singleton

我们正在使用内部简单的Logger类来处理应用程序的日志记录任务(.NET 3.5)。

记录器代码非常陈旧,设计与此类似:

public class Logger : ILogger
{
     private ILogger instance;

     private static ILogger Instance
     { 
         // Initialized on first use.
         get { return instance; }
     }

     public static void Debug(string msg)
     {
           instance.Debug(msg);
     }

     public static void Error(string msg)
     {
           ....
     }
}

实例本身在第一次使用时被初始化(懒惰)。

根据其严格的“书本”实现,这不是Singleton,但是,从所有调用代码访问此类是 静态访问

出于测试目的和其他架构原因,我希望能够用其他内容替换内部实例(注入它)。

我怎样才能轻松实现这一目标?我们目前没有使用任何IoC容器,但我不想将一个setter暴露给Instance属性,因为这会破坏整个Singleton之类的设计。

有关如何为此提出解决方案的任何建议吗?

5 个答案:

答案 0 :(得分:2)

考虑使用Fakes Framework进行测试。你可以使用类似这样的东西将调用存根到静态方法

ShimLogger.Instance = () => new LoggerMock();

对于.net 3.5,您可以使用Moles Framework来存根静态方法调用。配置代码如下所示:

MLogger.Instance = () => new LoggerMock();

需要将静态方法Instance设为public,但在此配置之后,每次调用static方法都将返回您的模拟实例。

答案 1 :(得分:1)

事实上,制定者并不是一个好的选择。

相反,我会考虑两种可能的方法。首先,explcit配置方法:

public class Logger : ILogger {

  public void ConfigureLogger( ILogger logger ) {
     this.instance = logger;
  }

}

这种方法的一个优点是意图明确,而且你必须以明确的方式调用这种方法。

另一种选择是允许你在配置中传递一种记录器:

<appSettings>
    <add key="loggerType" value="The.Type.From, Some.Assembly" />
</appSettings>

然后,在您的Logger类中重写初始化例程,以便在配置参数存在时,您更喜欢配置中提供的类型OVER默认类型。

这种方法的一个优点是您可以使用配置更改重新配置客户端,而无需更改代码。

无论如何,IoC容器不会咬人。介绍一个,因为它可以长期获得回报。

答案 2 :(得分:0)

我不会自己动手。我使用企业库几乎满足了我的所有日​​志记录需求。它适用于桌面和asp.net项目。 Asp.net可能会有点问题,因为你必须处理服务器上的安全性,但我已经完成了。

http://entlib.codeplex.com/

人们也喜欢Log4Net,但我从未使用它,因此我无法评论它。

答案 3 :(得分:0)

我会使用Logger修改代码。而不是通过Logger.Instance访问记录器,而是将所需的记录器实例传递给对象。然后在您的工厂和/或组合根中,将Logger.Instance作为生产代码中记录器的来源传递,在单元测试中,可以很容易地使用模拟记录器。

public class Foo
{
  private readonly ILogger logger;

  public Foo(ILogger logger)
  {
    if (logger == null)
      throw new ArgumentNullException("logger");
    this.logger = logger;
  }

  public void Func()
  {
    try
    {
      // do something
    }
    catch (Exception ex)
    {
      // call the provided logger dependency
      this.logger.WriteError(ex);

      // not the static singleton property
      Logger.Instance.WriteError(ex);
    }
  }
}

答案 4 :(得分:0)

另一个想法是为internal属性设置Instance setter,并使用InternalsVisibleTo属性使内部setter对测试程序集可见。请注意,如果包含记录器的程序集名称较强,则必须在PublicKey属性中指定InternalsVisibleTo。显然,这是最有帮助的(在不让其他开发人员意外地 - 或故意 - 将Instance设置为其他东西的意义上)如果您的记录器所在的是自己的程序集或某种基础结构程序集,其中大多数开发/日志记录未发生