Как реализовать чистые границы компонентов архитектуры в C ++? - PullRequest
2 голосов
/ 09 января 2020

Я читаю Чистую архитектуру Роберта C. Мы с Мартином хотели бы применить полученные в книге знания к моим программам на C ++. Однако я не понимаю, как должны работать границы интерфейса.

«Внутренние слои» (бизнес-правила) не должны ничего знать о внешних слоях. То, что находится над красной линией, не должно ничего знать о том, что ниже, на изображении ниже.

Но если бизнес-правила используют интерфейс C ++ (чистый абстрактный класс) для общения с базой данных (база данных) Интерфейс на изображении ниже), не должны ли они включать ссылку на заголовок реализации в модуле «Доступ к базе данных / База данных», поскольку не может быть создан экземпляр абстрактного базового класса? И не будет ли это нарушением принципа, согласно которому бизнес-правила не должны ничего знать о внешних слоях?

Каков правильный путь к этому в c ++?

Figure 17.2, Clean Architecture

Изображение: рисунок 17.2 из Чистой архитектуры

1 Ответ

3 голосов
/ 09 января 2020

Чистый виртуальный интерфейс в C ++ примерно аналогичен интерфейсу в управляемом языке, таком как C# или Java:

struct DatabaseInterface {
    virtual ~DatabaseInterface();
    virtual std::string get(std::string key) = 0;
    virtual void put(const std::string& key, const std::string& value) = 0;
};

Класс, зависящий от базы данных, может зависеть от указателя на абстрактный базовый класс. Ему не нужно ничего знать о реализации:

struct BusinessRules {
    std::unique_ptr<DatabaseInterface> db; // reference
    BusinessRules(std::unique_ptr<DatabaseInterface>);
    /* stuff using the database interface */
};

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

struct SpecificDatabaseAccess: public DatabaseInterface {
    SpecificDatabaseAccess(const std::string& connectionString);
    std::string get(std::string key) override;
    void put(const std::string& key, const std::string& value) override;
};

/* ... */

// dependency injection through the constructor
auto db = std::make_unique<SpecificDatabaseAccess>("...");
auto rules = BusinessRules(std::move(db));

Стандартная библиотека делает нечто подобное с istream. istream имеет несколько методов, которые работают поверх элемента streambuf более низкого уровня. streambuf является абстрактным интерфейсом, реализации которого обеспечивают доступ ввода / вывода (к stdin, файлам , строкам , et c).

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

...