0

我有一个名为 ILogger 的接口,它基本上包含一些日志记录方法。

记录器.cs

public interface ILogger
{   
    void LogError(string message, Exception exception = null);

    void LogMessage(string message);

    void LogValidationError(UploadResult uploadResult);

    void LogValidationError(ValidationResult validationResult);

    void LogProcessingError(string processingError);
}

我有一个实现此接口的 LogHelper 类。LogHelper 类通过 StructureMap 实例化,例如

ObjectFactory.Initialize(
    request =>
    {
        request.For<ILogger>().Singleton().Use<LogHelper>();

    });

我有很多类的构造函数,我只是实例化这个类并调用方法来记录信息。例如:我有一个类说 Dummy1 在其构造函数中我将 LogHelper 实例化为:

public Dummy1()
{
    this.logger = ObjectFactory.GetInstance<ILogger>();     
}

在 LogHelper 中,我有一个方法,它基本上创建日志文件并将作为参数传递的消息写入它。

public void LogMessage(string message)
{
    using (var writer = this.GetTextWriter(this.messageFilename))
    {
        writer.WriteLine(message);
    }
}

目前,文件名被硬编码为 LogHelper 类的常量属性private string messageFilename = "logs\\UserCreationResult.log"

但我希望在实例化 LogHelper 时动态发送文件名。我想拥有一个类属性,并在实例化类时在构造函数中定义该属性。但是由于 LogHelper 类被实例化为ObjectFactory.GetInstance<ILogger>(). 我无法调用可以传递文件名的构造函数。

4

2 回答 2

1

您将记录器用作单例,因此您不会在每次调用时都创建一个实例ObjectFactory.GetInstance<ILogger>();,您只是始终获得对同一个记录器实例的引用,该实例在第一次使用时创建一次。

如果要写入特定目的地,那么最好的解决方案是在Logging方法中指定目的地:

 void LogError(string message, 
               Exception exception = null,
               string destination = /*some adequate defualt value*/);

void LogMessage(string message,
                string destination = /*some adequate defualt value*/);

如果您从预期的方法同时使用记录器并因此设置不同的目的地,那么在您的记录器实例中创建具有特定目的地的状态信息可能会很危险;您最终可能会记录不应该记录的内容。

这带来了一个重要的问题;因为您正在跨应用程序(单例)共享记录器,所以如果有可能以这种方式调用它,请确保它的方法可以安全地同时调用。

于 2018-02-16T19:42:46.947 回答
1

不幸的是,你这样做的方式有点弄巧成拙。您的课程只知道ILogger,而不是任何特定的实现ILogger。这很好——这意味着实现可以写入文件、SQL 表或任何东西。

但是,如果您的班级只知道ILogger,而不知道实现,那么您的班级如何知道记录器需要文件路径?如果您将方法签名更改ILogger为包含文件路径,则会发生两件事。

  1. 不可能有任何不写入文件的实现ILogger除非它忽略文件路径,这真的很奇怪。)
  2. 现在调用记录器的类必须知道文件路径。该类将从哪里获得文件路径?它会存储在类中吗?在这种情况下,您最终会得到一个不起作用的类,除非它是在可以写入该确切文件路径的计算机上执行的程序集的一部分。

相反,在哪里以及如何记录的细节应该存在于你的ILogger实现中的某个地方。这更接近单一职责原则。调用的类ILogger不负责决定如何ILogger工作。它不知道,也不想知道。上面写着“来,拿走这个并记录下来。” 记录器实现负责其余部分。

我建议ObjectFactory完全废弃静态并使用容器来解析和创建所有类,包括记录器和依赖它的类,但这太宽泛了,它并没有真正的帮助。(它已被弃用,因为它是一种糟糕的模式。它甚至不在最新版本的 StructureMap 中。)

以上都是建议。在此之后,我提供了一个并非真正值得推荐的选项,但需要较少的更改,并使您的课程不知道文件路径,因为请不要这样做。

一种选择——折中的妥协——可能是注册不同的命名实现ILogger. 您可以将您的记录器类修改为如下所示:

public class FileLogger : ILogger
{
    private readonly string _filePath;

    public FileLogger(string filePath)
    {
        _filePath = filePath;
    }
}

现在您可以创建该记录器类的多个实例,并为每个实例传递不同的文件路径。这样它就不是一个静态属性,它限制你只有一个文件路径。

然后你可以像这样注册你的实现。

ObjectFactory.Initialize(
    request =>
    {
        request.For<ILogger>().Singleton()
            .Use<FileLogger>(() => new FileLogger("some path")).Name = "LoggerOne";

        request.For<ILogger>().Singleton()
             .Use<FileLogger>(() => new FileLogger("some other path")).Name = "LoggerTwo";

    });

现在你的班级可以说出它想要哪个记录器,如下所示:

var logger = ObjectFactory.GetNamedInstance<ILogger>("LoggerOne");

但也请不要真的这样做。这比我在这里能详细描述的要多,但是看看依赖注入,这样你的类就真的只知道ILogger而不知道或关心他们得到了哪个实现,而不告诉它如何去做工作。

于 2018-02-16T21:24:36.003 回答