【发布时间】:2011-01-29 19:53:08
【问题描述】:
我正试图弄清楚这在实践中是如何做到的,以免违反开放封闭原则。
假设我有一个名为 HttpFileDownloader 的类,它有一个函数,它接受一个 url 并下载一个将 html 作为字符串返回的文件。这个类实现了一个只有一个函数的 IFileDownloader 接口。因此,在我的整个代码中,我都引用了 IFileDownloader 接口,并且每当 IFileDownloader 被解析时,我的 IoC 容器都会返回一个 HttpFileDownloader 实例。
然后经过一段时间的使用,发现有时候服务器太忙了,会抛出异常。我决定要解决这个问题,如果遇到异常,我将自动重试 3 次,并在每次重试之间等待 5 秒。
所以我创建了 HttpFileDownloaderRetrier,它有一个函数在一个 for 循环中使用 HttpFileDownloader,最多 3 个循环,每个循环之间等待 5 秒。为了测试 HttpFileDownloadRetrier 的“重试”和“等待”能力,我通过让 HttpFileDownloaderRetrier 构造函数采用 IFileDownloader 来注入 HttpFileDownloader 依赖项。
所以现在我希望 IFileDownloader 的所有 Resolving 都返回 HttpFileDownloaderRetrier。但如果我这样做,那么 HttpFileDownloadRetrier 的 IFileDownloader 依赖项将获得一个自身的实例,而不是 HttpFileDownloader 的实例。
所以我可以看到我可以为 HttpFileDownloader 创建一个名为 IFileDownloaderNoRetry 的新接口,并更改 HttpFileDownloader 来实现它。但这意味着我正在更改违反 Open Closed 的 HttpFileDownloader。
或者我可以为 HttpFileDownloaderRetrier 实现一个名为 IFileDownloaderRetrier 的新接口,然后将我的所有其他代码更改为引用该接口而不是 IFileDownloader。但是,我现在在所有其他代码中都违反了 Open Closed。
那么我在这里错过了什么?如何在不更改现有代码的情况下用新的实现层(重试和等待)包装现有实现(下载)?
如果有帮助,这里有一些代码:
public interface IFileDownloader
{
string Download(string url);
}
public class HttpFileDownloader : IFileDownloader
{
public string Download(string url)
{
//Cut for brevity - downloads file here returns as string
return html;
}
}
public class HttpFileDownloaderRetrier : IFileDownloader
{
IFileDownloader fileDownloader;
public HttpFileDownloaderRetrier(IFileDownloader fileDownloader)
{
this.fileDownloader = fileDownloader;
}
public string Download(string url)
{
Exception lastException = null;
//try 3 shots of pulling a bad URL. And wait 5 seconds after each failed attempt.
for (int i = 0; i < 3; i++)
{
try { fileDownloader.Download(url); }
catch (Exception ex) { lastException = ex; }
Utilities.WaitForXSeconds(5);
}
throw lastException;
}
}
【问题讨论】:
标签: c# dependency-injection ioc-container open-closed-principle