SOLID+DIP+фабрика

Коллеги, вопрос по интерфейсам, инверсии зависимостей, созданию экземпляров классов. Допустим, есть "клиент" (main.cpp), которому нужны некоторые данные. Клиенту не важно, как он их получит: из памяти, из файла, по сети... Он работает исключительно с интерфейсом, т.е.

dataStorage.hpp

struct Data
{
    int a = 0;
    int b = 0;
    int c = 0;
};

// интерфейс
class DataStorage
{
public:
    virtual ~DataStorage() {};
    virtual Data getData() = 0;
};

main.cpp

#include <iostream>
#include "dataStorage.hpp"

using namespace std;

int main()
{
    DataStorage* dataStorage;

    Data data = dataStorage->getData();

    cout << "a = " << data.a << '\t' << "b = " << data.b << '\t' << "c = " << data.c << '\n';
    return 0;
}

Реализация интерфейса:

dataStorageImp.hpp

class DataFromMemory: public DataStorage
{
public:
    DataFromMemory();
    Data getData() override;
};


class DataFromFile: public DataStorage
{
public:
    DataFromFile();
    Data getData() override;
};

dataStorageImp.cpp

#include "dataStorageImp.hpp"


DataFromMemory::DataFromMemory() {};

Data DataFromMemory::getData()
{
    Data data;
    data.a = 1;
    data.b = 2;
    data.c = 3;
    return data;
}


DataFromFile::DataFromFile() {};

Data DataFromFile::getData()
{
    Data data;
    data.a = 10;
    data.b = 20;
    data.c = 30;
    return data;
}

Код не рабочий, это как бы заготовка. Вопрос, собственно, о том, как и где я должен создать экземпляры классов-реализаций DataFromMemory и DataFomFile? Выбор между ними можно сделать по условию if "fromFile": {} или if "from_memory": {}, или используя макрос #define USE_MEMORY, или USE_FILE. Не принципиально.


Ответы (1 шт):

Автор решения: HerrAskin

Я сделал следующую реализацию. Специалисты по c++ и SOLID, прокомментируйте, плз.

"main.cpp"

#define USE_DATA_FROM_MEMORY
//#define USE_DATA_FROM_FILE

#include <iostream>
#include "creator.hpp"

using namespace std;

int main()
{
    Creator* creator;
    DataStorage* dataStorage = creator->getInstance();

    Data data = dataStorage->getData();

    cout << "a = " << data.a << '\t' << "b = " << data.b << '\t' << "c = " << data.c << '\n';

    delete dataStorage;
    dataStorage = nullptr;

    return 0;
}

"creator.hpp"

#ifndef CREATOR_HPP
#define CREATOR_HPP

#include "dataStorageImp.hpp"

class Creator
{
public:
    DataStorage* getInstance()
    {
        #ifdef USE_DATA_FROM_MEMORY
        return new DataFromMemory();
        #endif // USE_DATA_FROM_MEMORY

        #ifdef USE_DATA_FROM_FILE
        return new DataFromFile();
        #endif // USE_DATA_FROM_FILE
    };
    ~Creator() = default;
};

#endif // CREATOR_HPP

"dataStorage.hpp"

#ifndef DATASTORAGE_HPP
#define DATASTORAGE_HPP

struct Data
{
    int a = 0;
    int b = 0;
    int c = 0;
};


class DataStorage
{
public:
    virtual Data getData() = 0;
    virtual ~DataStorage() {};
};

#endif // DATASTORAGE_HPP

"dataStorageImp.hpp"

#ifndef DATASTORAGEIMP_HPP
#define DATASTORAGEIMP_HPP

#include "dataStorage.hpp"

// это просто некая реализация
class DataFromMemory: public DataStorage
{
public:
    DataFromMemory() = default;
    Data getData() override
    {
        Data data;
        data.a = 1;
        data.b = 2;
        data.c = 3;
        return data;
    }
};

// другая реализация, другие значения
class DataFromFile: public DataStorage
{
public:
    DataFromFile() = default;
    Data getData() override
    {
        Data data;
        data.a = 10;
        data.b = 20;
        data.c = 30;
        return data;
    }
};

#endif // DATASTORAGEIMP_HPP

Еще раз повторюсь, смысл в том, чтобы клиент (main.cpp) получал данные не зависимо от источника этих данных, исключительно через интерфейс. Вопрос чисто теоретический, понять для себя, как это можно сделать.

→ Ссылка