Введение
Допустим, у нас есть набор объектов с некими данными, и нам нужно произвести манипуляции с этими объектами.
Положим, самый обычный пример — наполнить корзинку фруктами. Мы реализуем некий класс Сart, в который будем складывать фрукты. Далее нам понадобиться базовый класс Fruit, для того, чтоб определить параметр объема, которому мы будем присваивать значение в зависимости от фрукта. Задача решена, фрукты складываются до тех пор, пока объем корзинки не достигнет порога.
После нескольких итераций, когда наша программа, по наполнению корзинок, сложила несколько сгнивших груш, яблок с червями или еще что-нибудь пошло не так, мы расширим базовый класс Fruit, добавив в него параметры свежести, чистоты и т.д., пока будут появляется новые условия проверки.
И вот тут начинается проблема. Да, наш класс стал более универсальным, но увеличиваться может не только количество проблем с фруктами, но и сами ситуации сбора фруктов могут быть различными. Например, мы собираем фрукты там, где абсолютно каждый плод будет свежим. Зачем, в таком случае, нам поле, отвечающее за свежесть, если все фрукты заведомо свежие? Городить лишний функционал, для указания обязательных и не обязательных типов? — нет конечно. Мы выносим ряд опциональных типов в массив(словарь), где каждый тип не является непосредственной частью класса. Наш функционал снова стал умнее, отлично.
Однако, я решил пойти дальше, и немного развить эту тему.
Идея
Мы определяем тип, который будет отвечать за хранение опциональных переменных в строковом формате. Фактически, это обертка над массивом. В качестве минимального набора информации будем хранить название переменной, тип(это может быть как базовый, int, string, bool, так и композиция из нескольких базовых) и значение.
Методы у данного объекта могут быть вариативными, но я для себя выделил следующие:
-
получение списка переменных и их типов в строковом формате(в целом, можно ограничиться названиями). Это может пригодится если мы какой-то объект захотим отметить «началом координат».
-
геттер, который возвращает наш массив с информацией о переменных в формате константы.
-
получение id типа и значение по названию переменной(понадобиться для каста в конкретный тип).
class IVariable { public: using Type = std::variant < int, double, bool, std::string>; // для удобства каста типов virtual std::vector < std::pair < std::string_view, std::string_view > > abstract() = 0; virtual std::vector < std::tuple < std::string_view, VarTypes, std::string_view > > released() = 0; virtual std::pair < VarTypes, std::string_view > released(const std::string_view& name) = 0; };
Теперь, когда у нас все опциональные параметры обособлены в отдельный тип, мы можем проделать следующее:
-
Имея набор все тех же фруктов, проверить параметр «свежесть» у каждого фрукта, предварительно построив вектор только из этого параметра.
Вспомогательная функция, которая просто проверяет наличие конкретного параметра у массива наших «расширенных» переменных
bool tryVector(const std::string_view& dX) { auto type = range.front()->released(dX); if(type.first == VarTypes::_null) { return false; } for(auto&& var : _range) { if(var->released(dX).first != type.first) { return false; } } return true; }
-
Или наоборот, построить вектор сразу из нескольких параметров(скомбинировать несколько вызовов tryVector).
Вообще говоря, вспомогательных функций может быть и больше, но для примера я ограничился одной, дабы не захламлять статью.
Мотивация
-
Возможность оперировать с данными набора объектов, не имея информации о базовом типе, или вообще не имея базового типа.
-
Возможность регулировать скоуп работы с объектами, когда, в зависимости от ситуации, нам нужно либо больше параметров, либо наоборот меньше.
-
Думать не над тем, как спроектировать объект, а над тем, какие действия должны совершаться с объектом
Недостатки
-
Не подходит для расчета большого количества однотипных объектов. Скажем, для какого-нибудь класса, определяющего rgb составляющую пикселя — эффективнее будет явно определить свойства.
-
Подобный подход будет потреблять достаточно много памяти, и соответственно имеет смысл использовать его только для работы с объектами, суть которых нельзя выразить через несколько простых переменных, т.е. больше для логической обработки объектов, нежели прямых расчетов.
ссылка на оригинал статьи https://habr.com/ru/post/545940/
Добавить комментарий