我想知道在实践中如何做到这一点,以免违反开闭原则。
假设我有一个名为httpfiledownloader的类,它有一个函数接受一个url并下载一个以字符串形式返回html的文件。这个类实现了一个ifiledownloader接口,它只有一个函数。所以在我的代码中,我都引用了ifiledownloader接口,并且每当解析了ifiledownloader时,我的ioc容器都会返回httpfiledownloader的实例。
然后在使用了一段时间后,很明显有时服务器太忙,并抛出异常。为了解决这个问题,如果出现异常,我将自动重试3次,每次重试间隔5秒。
所以我创建了httpfiledownloaderretrier,它有一个函数在for循环中使用httpfiledownloader,最多3个循环,每个循环之间等待5秒。为了测试httpfiledownloaderretrier的“重试”和“等待”功能,我通过让httpfiledownloaderretrier构造函数使用ifiledownloader来注入httpfiledownloader依赖项。
所以现在我想让ifiledownloader的所有解析返回httpfiledownloaderretrier。但如果我这样做了,那么httpfiledownloaderretrier的ifiledownloader依赖项将获得自身的实例,而不是httpfiledownloader的实例。
所以我可以看到我可以为httpfiledownloader创建一个名为ifiledownloadernoretry的新接口,并更改httpfiledownloader来实现它。但这意味着我正在更改httpfiledownloader,它违反了open-closed。
或者我可以为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;
}
}
最佳答案
您或多或少地实现了断路器设计模式。与以往一样,在使用di实现横切关注点时,关键是应用Decorator模式。
像这样编写一个CircuitBreakingFileDownloader:
public class CircuitBreakingFileDownloader : IFileDownloader
{
private readonly IFileDownloader fileDownloader;
public CircuitBreakingFileDownloader(IFileDownloader fileDownloader)
{
if (fileDownloader == null)
{
throw new ArgumentNullException("fileDownloader");
}
this.fileDownloader = fileDownloader;
}
public string Download(string url)
{
// Apply Circuit Breaker implementation around a call to
this.fileDownloader.Download(url)
// here...
}
}
这种方法遵循开放/封闭原则,倾向于组合而不是继承。它还满足单一责任原则,因为断路器只处理这一方面,而装饰的IFileDownloader则专注于自己的责任。
大多数合适的di容器都理解decorator模式,因此现在可以通过返回包含真正httpfiledownloader的circuitbreakingfiledownloader来配置容器来解析对ifiledownloader的请求。
事实上,这种方法可以被广泛应用,你可以研究一个通用的断路器拦截器。Here's an example that uses Castle Windsor。
关于c# - 使用IoC和依赖注入(inject),如何在不违反开放-闭合原则的情况下,用新的实现层包装代码?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2483604/