Вычисление динамических объектов по вектору

от автора

Введение

Допустим, у нас есть набор объектов с некими данными, и нам нужно произвести манипуляции с этими объектами.

Положим, самый обычный пример — наполнить корзинку фруктами. Мы реализуем некий класс С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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *