我正在阅读单身模式的缺点.在许多论坛中建议的有效使用单例是Logging应用程序.我想知道为什么这是模式的有效使用.我们不是在整个应用程序中维护内存中的状态信息吗?
为什么不使用一个函数:
class Logger
{
public static void Log(string message)
{
//Append to file
}
}
Run Code Online (Sandbox Code Playgroud)
要回答“为什么不只使用函数”:此代码在多线程日志记录中工作不正确。如果两个线程尝试写入同一个文件,则会抛出异常。这就是为什么最好使用单例进行日志记录的原因。在这个解决方案中,我们有一个线程安全的单例容器,其他线程安全地将消息(日志)推送到容器中。并且容器(始终是线程安全队列)将消息/日志一一写入文件/db/etc。
最好声明接口:
interface ILogger
{
public void Log(string message);
}
Run Code Online (Sandbox Code Playgroud)
然后实现特定类型的logger
class FileLogger : ILogger
{
public void Log(string message)
{
//Append to file
}
}
class EmptyLogger : ILogger
{
public void Log(string message)
{
//Do nothing
}
}
Run Code Online (Sandbox Code Playgroud)
并在需要的地方注射。您将注入EmptyLogger测试。使用单例将使测试变得更加困难,因为您也必须在测试中保存到文件。如果您想测试类是否生成正确的日志条目,您可以使用模拟并定义期望。
关于注射:
public class ClassThatUsesLogger
{
private ILogger Logger { get; set; }
public ClassThatUsesLogger(ILogger logger) { Logger = logger }
}
Run Code Online (Sandbox Code Playgroud)
ClassThatUsesLogger 在生产代码中采用 FileLogger:
classThatUsesLogger = new ClassThatUsesLogger(new FileLogger());
Run Code Online (Sandbox Code Playgroud)
在测试中它需要 EmptyLogger:
classThatUsesLogger = new ClassThatUsesLogger(new EmptyLogger());
Run Code Online (Sandbox Code Playgroud)
您可以在不同的场景中注入不同的记录器。有更好的方法来处理注入,但您必须阅读一些内容。
编辑
请记住,您仍然可以在代码中使用单例,正如其他人所建议的那样,但是您应该将其用法隐藏在接口后面,以放松类与日志记录的特定实现之间的依赖关系。