5

在我的设计中,我已经到了一个重点,我正在认真考虑单例。

众所周知,“常见”的论点是“永远不要这样做!这太可怕了!”,就好像我们在代码中乱扔一堆goto语句一样。

ServiceStack是一个很棒的框架。我自己和我的团队都在使用它,并且我们有一个复杂的基于 Web 服务的基础架构要实施。我一直在鼓励异步设计,并尽可能SendAsync在服务堆栈客户端上使用。

鉴于我们有所有这些不同的系统在做不同的事情,我突然想到我想要一个通用的记录器,(实际上是一个 Web 服务本身,如果 Web 服务不可用,则回退到本地文本文件- 例如,一些恶魔正在跟踪建筑物)。虽然我是依赖注入的忠实拥护者,但将“使用此记录器客户端”的引用传递给每个异步请求似乎并不干净(至少对我而言)。

鉴于 ServiceStack 的失败签名是一个Func<TRESPONSE, Exception>(并且我对此没有错),我什至不确定最初进行调用的封闭方法是否具有有效的句柄。

但是,如果此时我们有一个单例记录器,那么我们在世界的哪个位置、我们在哪个线程上以及我们在无数匿名函数的哪个部分都无关紧要。

这是一个公认的有效案例,还是一个非争论 - 单例?

4

4 回答 4

3

单例的经典实现只有一个问题——它很容易访问,并且可以直接使用,这会导致强耦合、上帝对象等。

在经典实现下,我的意思是:

class Singleton
{
   public static readonly Singleton Instance = new Singleton();
   private Singleton(){}
   public void Foo(){}
   public void Bar(){}
}

如果您仅在对象生命周期策略方面使用单例,并让 IoC 框架为您管理它,保持松散耦合 - 只要您在应用程序的整个生命周期中拥有“只有一个”类的实例就没有错确保它是线程安全的。

于 2013-04-24T21:05:53.083 回答
3

日志记录是单例有意义的领域之一,它永远不会对您的代码产生任何副作用,并且您几乎总是希望全局使用相同的记录器。使用 Singleton 时您应该关心的主要事情是 ThreadSafety,在大多数 Logger 的情况下,它们默认是 ThreadSafe。

ServiceStack 的 Logging API允许您通过在 App_Start 上全局配置它来提供可替代的 Logging 实现:

LogManager.LogFactory = new Log4NetFactory(configureLog4Net:true);

此后,每个类现在都可以访问上面 Factory 中定义的 Log4Net 记录器:

class Any
{
    static ILog log = LogManager.GetLogger(typeof(Any));
}

在所有测试项目中,我更喜欢将所有内容都记录到控制台,所以我只需要设置一次:

LogManager.LogFactory = new ConsoleLogFactory();

默认情况下,ServiceStack.Logging 会记录到忽略每个日志条目的良性 NullLogger。

于 2013-04-24T21:18:51.813 回答
0

如果您将通用日志记录放置在应用程序代码调用的静态外观之后,请问问自己您将如何实际对该代码进行单元测试。这是依赖注入试图解决的一个问题,但是您通过让应用程序逻辑依赖于静态类来重新引入它。

您可能还会遇到另外两个问题。我要问你的问题是:你确定你没有记录太多,你确定你没有违反 SOLID 原则。

一年前我写了一个SO 答案来讨论这两个问题。我建议你阅读它。

于 2013-04-24T20:58:12.313 回答
0

和往常一样,我更喜欢有一家工厂。这样我可以在将来更改实现并维护客户合同。

你可以说单例的实现也可以改变,但工厂只是更通用。例如,工厂可以实施任意生命周期策略,并随着时间或根据您的需要更改此策略。另一方面,虽然这在技术上可以为单例实现不同的生命周期策略,但您得到的可能不应该被视为“单例”,而是“具有特定生命周期策略的单例”。这可能和听起来一样糟糕。

每当我要使用单例时,我首先考虑的是工厂,大多数时候,工厂只是胜过单例。如果你真的不喜欢工厂,那就创建一个静态类——一个只有静态方法的无状态类。很有可能,你不需要一个对象,只需要一组方法。

于 2013-04-24T20:59:03.083 回答