Обертка API для поддержки внедрения зависимостей

Я взаимодействую с API, который имеет только статические функции и не может быть открыт и изменен.

    public class WindowsNativeGraphAPI
    {
        public static IEnumerable<IGraphData> GetGraphData();
        public static bool DeleteGraphData(IGraphData data);
    }

Я хотел бы иметь возможность передавать API в функцию или конструктор и соблюдать внедрение зависимостей (на случай, если позже мы заменим API).

public void GatherGraphData(IGraphAPI api)
{...}

Чтобы разрешить передачу этого API в качестве параметра, мне нужно как минимум абстрагироваться, чтобы использовать интерфейс для передачи в функцию.

    public interface IGraphAPI
    {
        IEnumerable<IGraphData> GetGraphData();
        bool DeleteGraphData(IGraphData data);
    }

Однако тогда мне нужно будет реализовать интерфейс в другом классе, поскольку я не могу изменить исходный API. Этот класс будет легкой оболочкой для API, которая просто вызывает соответствующую функцию в API и возвращает тот же результат.

    public class WindowsGraphAPI : IGraphAPI
    {
        public IEnumerable<IGraphData> GetGraphData()
        {
            return WindowsNativeGraphAPI.GetGraphData();
        }

        public bool DeleteGraphData(IGraphData data)
        {
            return WindowsNativeGraphAPI.DeleteGraphData(data)
        }
    }

Мне не нравится идея создания еще одного класса для обертывания API. Я понимаю, что эта оболочка будет очень легкой и будет просто возвращать результаты API, но как мне протестировать эту оболочку? Оболочка, вероятно, также должна содержать некоторую обработку исключений, чтобы справляться с ошибками в API. Если бы мы перешли на другой API, который столкнулся с той же проблемой, нам пришлось бы снова создавать эти дополнительные классы и интерфейсы.

В идеале конечным результатом должен быть фиктивный API, который можно использовать при написании модульных тестов для нового компонента, который его использует.

Это правильный способ сделать это? Можно ли это сделать по-другому?

Спасибо


person fletcher    schedule 16.07.2010    source источник


Ответы (2)


Да, это правильный путь. Новый интерфейс API и прокси-класс инкапсулируют решение о том, какую лежащую в основе библиотеку использовать, — единая обязанность.

person Pontus Gagge    schedule 16.07.2010
comment
+1 ASP.NET MVC делает это широко, поэтому он предоставляет набор примеров, демонстрирующих этот подход. - person Mark Seemann; 16.07.2010

Да, это правильный путь. Я бы не стал помещать обработку исключений в вашу оболочку, все, что вы делаете, это создаете класс, который вызывает статические методы, чтобы вы могли использовать DI. Вы хотите, чтобы оболочка вызывала те же исключения при тех же обстоятельствах, что и класс API со статическими методами. Таким образом, вы можете использовать ту же обработку исключений в своем методе, как если бы вы вызывали класс со статическими методами. Затем вы можете создавать те же исключения при тех же обстоятельствах в своем фиктивном классе API и тестировать обработку исключений.

person Ben Robinson    schedule 16.07.2010
comment
Я не могу согласиться с этими утверждениями об исключениях. Исключения являются частью функционального контракта API. Если типы исключений, создаваемые адаптированным API, определены в пакете, содержащем сам адаптированный API, разрешение этих исключений приведет к утечке деталей реализации через абстракцию. Лучше заключить их в исключения, которые определяются вместе с интерфейсом. - person Mark Seemann; 16.07.2010
comment
Это звучит фантастически с академической точки зрения, но на практике вы либо сталкиваетесь с недостаточной детализацией в ваших абстрактных исключениях, что делает проблемы отладки в реальном коде занозой в заднице, либо вам приходится выполнять массу работы по созданию сложной обработки исключений для предоставьте достаточно деталей для отладки позже, просто чтобы учесть тот факт, что вы МОЖЕТЕ в какой-то момент полностью изменить реализацию, а не просто издеваться над Windows API для модульного тестирования. - person Ben Robinson; 16.07.2010
comment
Я не говорю, что оболочка должна оборачивать исключения, создаваемые адаптированным API. Я хочу сказать, что клиент не должен полагаться на типы исключений, определенные адаптированным API, так как это создаст тесную связь. DI — это гораздо больше, чем возможность тестирования. - person Mark Seemann; 16.07.2010
comment
Я понимаю вашу точку зрения, но вы должны сделать это очень осторожно, убедившись, что вся соответствующая информация зарегистрирована и обработана правильно. Я только что потратил слишком много времени, желая, чтобы исключения из какого-то базового API попадали в файл журнала в чистом виде, потому что без них у меня почти невозможное время, пытаясь понять, почему какой-то код не работает. - person Ben Robinson; 16.07.2010
comment
наличие вложенного члена исключения вместе с достойным ведением журнала превращает это из академического в в высшей степени практическое удобство обслуживания. - person Pontus Gagge; 17.07.2010