Проблема проектирования IoC/SRP

Я разрабатываю систему, которая предоставляет веб-службу для доступа к своей базе данных и предоставляет ее через классы .NET. Наш обычный способ работы заключается в создании экземпляра класса веб-службы всякий раз, когда нам нужно получить доступ к базе данных и напрямую использовать этот экземпляр; это, конечно, полностью противоречит IoC и создает в основном непроверяемый код. Сейчас я пытаюсь разработать новый стандартный способ работы с использованием IoC, чтобы мы могли писать (больше) SOLID-кода.

Мое текущее решение таково (не очень хорошо объяснено):

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

Когда мне нужен доступ к базе данных в реальном приложении, я наследую от этого класса (вызывая новый класс, скажем, ApplicationDatabaseConnection) и реализую необходимые взаимодействия с базой данных в методах, которые затем могут вызывать веб-службу.

Этот класс не используется непосредственно в приложении, но предоставляет интерфейсы «соединителя» для различных частей приложения, каждая из которых представлена ​​моделью контроллера/представления, подобной классу на верхнем уровне. Всякий раз, когда вызывается одна из этих функций приложения (например, из пользовательского интерфейса), создается соответствующий объект контроллера и передается мой объект ApplicationDatabaseConnection в качестве реализации соответствующего интерфейса «соединителя», поэтому доступ к базе данных в этот момент правильно инкапсулируется и отделяется, как насколько я могу судить.

Моя проблема с этим такова: хотя это первый случай, когда я нашел фактическое использование ISP (принцип разделения интерфейса) в моем собственном коде (хотя я далеко не уверен, действительно ли это разумное использование концепции), я боятся, что этот класс может сделать слишком много и нарушить SRP (принцип единой ответственности). Или «только предоставление доступа к базе данных, хотя и для нескольких разных потребителей» является единой обязанностью?

Чтобы, возможно, сделать это немного яснее, вот примерно как будут выглядеть соответствующие классы:

DatabaseConnection
    protected m_webservice
    public OftenUsedDatabaseAccess()

ApplicationDatabaseConnection : DatabaseConnection, IConnectorA, IConnectorB
    public IConnectorA.RetrieveRecords(fieldValue)
    public IConnectorB.WriteStuff(listOfStuff)

FunctionAController
    private m_IConnectorA

FunctionBController
    private m_IConnectorB

Альтернативы, которые я могу придумать, тоже не кажутся мне идеальными.

Чтобы разделить функциональность доступа к базе данных, класс ApplicationDatabaseConnection может быть только фабричным классом с Create методами для разных соединителей (за интерфейсами IConnectorAFactory, IConnectorBFactory), но на самом деле там нет ничего, что требовало бы фабричного шаблона; я ничего не знаю только при создании экземпляров объектов «контроллер».

Кроме того, фактические классы коннекторов, по сути, также должны быть производными от DatabaseConnection, потому что им нужны одни и те же базовые способности, и тогда (самое позднее) вся конструкция становится довольно зловещей.

Я полагаю, что в какой-то момент своего мышления я свернул не в ту сторону, и теперь я полностью на ложном пути. Как должна выглядеть структура такого решения? Любой толчок в правильном направлении будет принят с благодарностью.

Изменить:

Ответ @Tobias заставил меня понять, что я забыл важную деталь: существуют две разные версии веб-службы с практически одинаковыми возможностями, но совершенно разными API, что является одной из причин, почему он должен быть инкапсулированы.

Другой момент заключается в том, что если мои логические классы обращаются к веб-службе напрямую (или через интерфейс), они также занимаются построением запросов веб-службы во всех их деталях, что делает код гораздо более тесно связанным (такой, какой мы использовали). производит до сих пор) и нарушает SRP - отсюда и идея интерфейса коннектора.


person TeaDrivenDev    schedule 18.02.2012    source источник


Ответы (2)


Почему бы просто не создать интерфейс, например MyWebService, и не реализовать его в MyWebServiceImpl, который вызывает ваш сервис? Можно было бы выставить те же методы, что и ваш сервис, и просто делегировать ему...

person Tobias    schedule 18.02.2012

Много месяцев спустя я знаю, что подход «ApplicationDatabaseConnection» нарушал не только SRP, но и OCP, дополнительно препятствуя модуляризации.

Путь, который я в конечном итоге выбрал, был немного похож на описанную мной «фабричную» альтернативу — «основной» объект DatabaseConnection относится к подклассу, который также имеет метод «Создать», который берет фабрики для конкретных производных DatabaseConnection и не заботится о что именно он создает. Таким образом, каждый контроллер может запрашивать свой собственный объект подключения, и могут быть добавлены контроллеры, о которых основное приложение не знает (например, через MEF).

person TeaDrivenDev    schedule 23.09.2012