Объединяйте хранилища через операции над логическим ключом и данными, а не через имена SaveToFile/SaveToDB. Конкретные пути, подключения и протоколы остаются внутри адаптеров.
Разработка единого интерфейса доступа к различным хранилищам
Объединяйте хранилища через операции над логическим ключом и данными, а не через имена SaveToFile/SaveToDB. Конкретные пути, подключения и протоколы остаются внутри адаптеров.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо выделить общую абстракцию и спроектировать единый интерфейс, который позволит одинаково работать с файлами, базой данных и удалённым сервером.
class FileManager {
public:
void SaveToFile(const std::string& filePath);
void ReadFromFile(const std::string& filePath);
};
class DBManager {
public:
void SaveToDB(const std::string& url);
void ReadFromDB(const std::string& url);
};
class ServerManager {
public:
void SaveToServer(const std::string& url);
void ReadFromServer(const std::string& url);
};
Опишите, какие методы и типы следует включить в абстракцию, как её реализовать и как обеспечить возможность расширения под новые типы хранилищ без изменения клиентского кода.
Ответ
Объединяйте хранилища через операции над логическим ключом и данными, а не через имена SaveToFile/SaveToDB. Конкретные пути, подключения и протоколы остаются внутри адаптеров.
Вариант для ограниченных по размеру двоичных объектов:
using Bytes = std::vector<std::byte>;
class IStorage {
public:
virtual Bytes Read(std::string_view key) = 0;
virtual void Save(std::string_view key, const Bytes& data) = 0;
virtual ~IStorage() = default;
};
FileStorage, DatabaseStorage и ServerStorage реализуют интерфейс, а клиент получает IStorage как зависимость. Нужно одинаково определить отсутствие ключа, перезапись, ошибки и гарантии атомарности. Если источник не умеет писать, выделите интерфейс чтения отдельно. Для больших объектов вместо полной загрузки нужен потоковый контракт.