Я пытаюсь выяснить, как это будет сделано на практике, чтобы не нарушать принцип Открыто-Закрыто.
Скажем, у меня есть класс с именем HttpFileDownloader, в котором есть одна функция, которая принимает URL-адрес и загружает файл, возвращающий html в виде строки. Этот класс реализует интерфейс IFileDownloader, который имеет только одну функцию. Поэтому во всем моем коде у меня есть ссылки на интерфейс IFileDownloader, и мой контейнер IoC возвращает экземпляр HttpFileDownloader всякий раз, когда разрешен IFileDownloader.
Затем после некоторого использования становится ясно, что иногда сервер слишком занят в данный момент и выдается исключение. Я решил, что, чтобы обойти это, я собираюсь автоматически повторить попытку 3 раза, если получу исключение, и подождать 5 секунд между каждой попыткой.
Поэтому я создаю HttpFileDownloaderRetrier, в котором есть одна функция, которая использует HttpFileDownloader в цикле for с максимум 3 циклами и 5-секундным ожиданием между каждым циклом. Так что я могу проверить возможности «повторять» и «ждать» HttpFileDownloadRetrier. У меня есть зависимость HttpFileDownloader, введенная с помощью конструктора HttpFileDownloaderRetrier, принимающего IFileDownloader.
Так что теперь я хочу, чтобы все Resolving of IFileDownloader возвращал HttpFileDownloaderRetrier. Но если я сделаю это, то зависимость IFileDownloader HttpFileDownloadRetrier получит экземпляр самого себя, а не 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;
}
}