我想知道在实践中如何做到这一点,以免违反开闭原则。
假设我有一个名为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/

10-12 12:41