@aDron1990

Как создать абстрактную фабрику, не ссылаясь на реализацию?

Здравствуйте, Хабровчане!

Я новичок, пишу на с++. Недавно решил поглубже разобраться в Архитектуре ПО, купил книжечку "Чистая Архитектура" Роберта Мартина. В целом материал понятен, но есть одна вещь, которую я либо пропустил, либо не понял. Суть проблемы:

Допустим, я создаю на с++ приложение Application.exe и библиотеку Service.dll. Ну или их Linux аналоги с соответствующими расширениями - это неважно. Важно то, что у нас есть два компонента, причем, следуя материалу из книги, компонент Application не должен ничего знать о компоненте Service - что буквально значит, что в проекте Application не должно быть #include файлов из проекта Service, а в обратном порядке включать файлы мы уже можем.
Делается это путем создания в Application интерфейса IService, и реализации его в ServiceImpl.
Однако, если мы захотим создать экземпляр ServiceImpl со стороны Application, нам все равно придется ссылаться на ServiceImpl.h, что нарушает направление зависимости.
Для решения этой задачи применяется шаблон "фабрика" - объект, который порождает наш ServiceImpl, и отдает нам в виде ссылки на IService. Реализуется он так же, путем создания интерфейса и его реализации.

В итоге мы должны иметь такую картину:

65bff48793eec114032456.png
И казалось бы - все хорошо, бери фабрику, и создавай объект ServiceImpl, при этом не завися от его внутреностей, и пользуйся на здоровье. Однако...
Тут и наступает момент, который я не понимаю: для того, чтобы создать Объект ServiceImpl мы используем фабрику, которую нужно тоже создать. А чтобы создать ее, нам нужно прописать буквально:
#include <IServiceFactory.h>           // это окей
#include <ServiceFactoryImpl.h>        // а это нет
...
IServiceFactory* factory = new ServiceFactoryImpl{};

Я вынужден ссылаться на ServiceFactiryImpl для создания фабрики. Но ведь это уже внутренние детали компонента Service! И это снова ломает направление зависимости.

Скажите, пожалуйста, что я делаю не так, и как этим пользоваться правильно?
  • Вопрос задан
  • 80 просмотров
Решения вопроса 1
AshBlade
@AshBlade
Просто хочу быть счастливым
В приложении, которое использует Dependency Injection можно выделить 2 "области":
- Composition Root
- Остальное приложение

Composition Root - это место в котором собираются все зависимости воедино. О всех реализациях зависимостей должен знать только он.
Остальное приложение - это (тафтология) все остальное приложение. Они используют только интерфейсы и другие абстрации.

Обычно Composition Root - это входная точка приложения. В нем создается эта самая ServiceFactoryImpl (стандартные реализации, из конфигурации могут быть прочитаны флаги, создан в зависимости от платформы и т.д.), а затем как интерфейс IServiceFactory, передается в остальные места.

Т.е. в коде это будет как-то так
#include <IServiceFactory.h>

class Sample {
private:
    IServiceFactory _factory;
public:
    Sample(IServiceFactory factory): _factory(factory) { }
    DoSomething() { 
        auto service = _factory.CreateService();
        service.MakeStuff();
    }
}

int main() {
    auto factory = ServiceFactoryImpl(); // Читаем конфигурацию, параметры ОС и др.
    auto sample = Sample(factory);
    sample.DoSomething();
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Alexandroppolus
@Alexandroppolus
кодир
Да, где-то в "верхних слоях кода" тебе всё равно надо будет явно сослаться на ServiceFactoryImpl, от этого не уйти. Но это совсем маленький "грязный" участок. А в остальном коде ты спокойно работаешь с этим экземпляром IServiceFactory и создаваемыми с помощью него имплементациями IService
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы