在 OOP 中使用可选的单例?
Using optional singletons in OOP?
我正在 .NET 中编写 PCL,我有一个围绕 HttpClient
的包装器 class,它以多种不同的方法从 URI 加载 HtmlAgilityPack.HtmlDocument
。它是 stateless,所以我真的很想让它成为静态的,因为在我看来用 new
实例化一些东西给人的印象是它包含状态。但是,我有几个我希望它继承的接口,所以它不能是静态的。这是我想到让它成为单身人士的地方。以下是代码中的一些片段:
public class ConcurrentClient : IAsyncClient<HtmlDocument>
{
private static readonly ConcurrentClient _Instance = new ConcurrentClient();
private ConcurrentClient() { }
public static ConcurrentClient Instance
{
get { return _Instance; }
}
public HtmlDocument LoadUri(string uri)
{
return LoadUriAsync(uri).Result;
}
// ...
public async Task<HtmlDocument> LoadUriAsync(string uri,
Encoding e, NetworkCredential creds, Action<HtmlDocument> prehandler)
{
// ...
}
}
不过,我想知道是否应该将开头部分更改为:
private static readonly ConcurrentClient _SharedInstance = new ConcurrentClient();
public static ConcurrentClient SharedInstance
{
get { return _SharedInstance; }
}
原因是我不太确定使用单例模式,主要是因为我很少看到它在其他库中使用(也许是 WinRT 的 Application.Current
?),我认为它会鼓励我的 PCL 的用户编写耦合代码,因为在任何地方调用 ConcurrentClient.Instance
比将它作为参数传递要容易得多。
但是, 我确实想鼓励使用共享实例,因为排除上述原因,调用 new ConcurrentClient()
没有什么意义,因为它所做的只是创建更多的内存开销。另外,我想不出更好的方法来使用不真正依赖状态的方法来实现继承。
您的单例已经实现了 2 个接口。真正的问题是,这个 Singleton 的依赖项在哪里,为什么存在?
如果答案是存在这些依赖关系是因为它们需要实现这些接口,那么我会说这是错误的。
进行 SOLID 设计的全部要点是依赖于接口而不是依赖于任何具体的实现。因此,任何需要这两个接口中的任何一个的人都应该通过依赖注入获得这些接口。所以这意味着接口将由它们的构造函数或通过方法调用中的额外参数、策略模式、...
另请参阅:http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx
创建单例可能是有原因的,但根据您的解释,这还不是很清楚。
花更多时间调查使用依赖注入。如果您控制住了它,请更进一步并研究如何使用控制反转容器。
Also, it's easy to forget DI and passing around the object as a parameter when you can just access it by Singleton.Instance.
您忘记了单元测试。如果您将接口传递给 class 构造函数,您可以轻松地模拟这些接口并测试您的 class 功能。有了你的单身人士,你的 classes 真的需要那个单身人士。单元测试会更难。
当然 Instance 很容易访问,它是全局的,而且由于人们总是回到对对象编程的旧习惯,这就是它如此受欢迎的原因。
我正在 .NET 中编写 PCL,我有一个围绕 HttpClient
的包装器 class,它以多种不同的方法从 URI 加载 HtmlAgilityPack.HtmlDocument
。它是 stateless,所以我真的很想让它成为静态的,因为在我看来用 new
实例化一些东西给人的印象是它包含状态。但是,我有几个我希望它继承的接口,所以它不能是静态的。这是我想到让它成为单身人士的地方。以下是代码中的一些片段:
public class ConcurrentClient : IAsyncClient<HtmlDocument>
{
private static readonly ConcurrentClient _Instance = new ConcurrentClient();
private ConcurrentClient() { }
public static ConcurrentClient Instance
{
get { return _Instance; }
}
public HtmlDocument LoadUri(string uri)
{
return LoadUriAsync(uri).Result;
}
// ...
public async Task<HtmlDocument> LoadUriAsync(string uri,
Encoding e, NetworkCredential creds, Action<HtmlDocument> prehandler)
{
// ...
}
}
不过,我想知道是否应该将开头部分更改为:
private static readonly ConcurrentClient _SharedInstance = new ConcurrentClient();
public static ConcurrentClient SharedInstance
{
get { return _SharedInstance; }
}
原因是我不太确定使用单例模式,主要是因为我很少看到它在其他库中使用(也许是 WinRT 的 Application.Current
?),我认为它会鼓励我的 PCL 的用户编写耦合代码,因为在任何地方调用 ConcurrentClient.Instance
比将它作为参数传递要容易得多。
但是, 我确实想鼓励使用共享实例,因为排除上述原因,调用 new ConcurrentClient()
没有什么意义,因为它所做的只是创建更多的内存开销。另外,我想不出更好的方法来使用不真正依赖状态的方法来实现继承。
您的单例已经实现了 2 个接口。真正的问题是,这个 Singleton 的依赖项在哪里,为什么存在?
如果答案是存在这些依赖关系是因为它们需要实现这些接口,那么我会说这是错误的。
进行 SOLID 设计的全部要点是依赖于接口而不是依赖于任何具体的实现。因此,任何需要这两个接口中的任何一个的人都应该通过依赖注入获得这些接口。所以这意味着接口将由它们的构造函数或通过方法调用中的额外参数、策略模式、...
另请参阅:http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx
创建单例可能是有原因的,但根据您的解释,这还不是很清楚。
花更多时间调查使用依赖注入。如果您控制住了它,请更进一步并研究如何使用控制反转容器。
Also, it's easy to forget DI and passing around the object as a parameter when you can just access it by Singleton.Instance.
您忘记了单元测试。如果您将接口传递给 class 构造函数,您可以轻松地模拟这些接口并测试您的 class 功能。有了你的单身人士,你的 classes 真的需要那个单身人士。单元测试会更难。 当然 Instance 很容易访问,它是全局的,而且由于人们总是回到对对象编程的旧习惯,这就是它如此受欢迎的原因。