Автоматизация создания объектов при помощи конвейера

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр!

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

Введение

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

На примере транспортных средств

Есть у нас набор атомарных компонентов:

	class EngineA;
	class EngineB;

	class WingsA;
	class WingsB;

	class FrameA;
	class FrameB;

	class WheelsA;
	class WheelsB;

и т.д.

Если нам нужна машина - мы просто объявляем класс Car, в котором есть нужный корпус, колеса, двигатель и т.д. Аналогично с какой-нибудь лодкой лодкой, мы бы объявили класс Boat, и быстренько набросали агрегацию нужных частей лодки.

Если же нам нужна и лодка, и машина, и даже самолет - мы можем воспользоваться паттерном фабрика, но что делать, если нам нужны машины, лодки, самолеты, и мы заранее не знаем сколько, когда, и в каком порядке.

Можно под каждую задачу писать отдельную агрегацию, предусмотрев классы Car, Boat, Plane с общим интерфейсом ITransport. Если нам нужно 2 машины, и 5 лодок - мы можем через цикл создать необходимое количество объектов. Однако, если нам важна последовательность, создавать подряд однотипные объекты через циклы уже не так просто. И чем больше параметров - тем больше времени нужно будет на написание того или иного кода для очередного шаблонного транспортного средства

Попробуем сэкономить это время, поставив создание объектов на поток!

Положим, у нас есть некий интерфейс ITransport

class ITransport
{
	public:

	virtual void move() = 0; 
};

реализованный в таких классах как Car, Boat, Plane.

class Car final : public virtual ITransport
{
public:

	Car() = default; 
	~Car() = default;

	void move() override
	{
		std::cout << "Car is move" << std::endl;
		// do something with parts
	}
      
 private:
      
    std::unique_ptr < IFrame >	_frame; 
    std::unique_ptr < IWheels >	_wheels; 
		std::unique_ptr < IEngine > _engine;
};


class Boat final : public virtual ITransport
{
public:

	Boat() = default; 
	~Boat() = default;

	void move() override
	{
		std::cout << "Boat is move" << std::endl;
		// do something with parts
	}
      
private:

	std::unique_ptr < IFrame >	_frame; 
	std::unique_ptr < IEngine>  _engine;
};

class Plane final : public virtual ITransport
{
public:

	Plane() = default; 
	~Plane() = default;

	void move() override
	{
		std::cout << "Plane is move" << std::endl;
		// do something with parts
	}

private:

	std::unique_ptr < IFrame > _frame;
	std::unique_ptr < IEngine> _engine;
	std::unique_ptr < IWings > _wings;
};

И нам нужно сделать 2 машины, лодку, 3 самолета и еще одну лодку, и именно в такой последовательности.

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

enum class VehTypes
{
	Car,
	Boat,
	Plane
};
	 
static std::map < std::string, VehTypes > VehCast{
	{"Car", VehTypes::Car},
	{"Boat", VehTypes::Boat},
	{"Plane", VehTypes::Plane}
};

Теперь, сам класс конвейера.

class Conveyor final
{
		
public:
			 
	using _TyParameters = std::map < std::string, std::string >; 
	using _TyStorage = std::vector < _TyParameters >;

	Conveyor(const _TyStorage& blueprints)
		: _blueprints(blueprints) { }

	~Conveyor() = default;

	std::vector < Vehicles::ITransport* > vehicles()
	{
		std::vector < Vehicles::ITransport* > result;

		for (auto&& blueprint : _blueprints)
		{

			switch (VehCast[blueprint["type"]])
			{
				case VehTypes::Car: result.emplace_back(new Vehicles::Car());
				break;

				case VehTypes::Boat: result.emplace_back(new Vehicles::Boat());
				break;

				case VehTypes::Plane: result.emplace_back(new Vehicles::Plane());
				break;
			}
		}

		return result;
	}
			 
private: 

	_TyStorage _blueprints;
};

Исходя из нежелания сильно переусложнять пример я опущу код интерпретатора для построения объекта из текстовых данных, а также прочие однотипные моменты касательно создания объектов.

Для удобства понимания переопределяем два новых типа:

  • Параметры - словарь с информацией о транспортном средстве

  • Хранилище - массив с информацией об объектах, в нужной нам последовательности.

using _TyParameters = std::map < std::string, std::string >; 
using _TyStorage = std::vector < _TyParameters >;

В конструктор мы лишь передаем информацию о том, какие транспортные средства нам нужны(их описание).

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

Проверяем

Создаем необходимый список(над этим можно отдельно поработать) транспортных средств с их параметрами.

Создаем конвейерный класс и передаем ему этот список.

Получаем набор готовых объектов.

Conveyor::_TyStorage blueprints
{
  {
    {"type", "Car"}, {"engineType", "EngineA"}, {"wheelsType", "WheelsB"}, etc..
  },		
  {
    {"type", "Car"},  
  },		
  {
    {"type", "Boat"},  
  },		
  {
    {"type", "Plane"},  
  },		
  {
    {"type", "Plane"},  
  },		
  {
    {"type", "Plane"}
  },		
  {
    {"type", "Boat"}
  },
};

Conveyor conveyor(blueprints);

for (auto&& transport : conveyor.vehicles())
{
	transport->move();
}

Результат

В итоге

И хотя мы тратим не сильно меньше времени на описание композиции атомарных объектов для каждой операции, суммарно, мы потратим на много меньше времени на процесс описания всего набора операций.

Данный способ не отличается идеальной производительностью, и по началу требует время на описание всех необходимых компонентов, однако в дальнейшем снижает суммарное время на описание новых последовательностей обработки данных/конструирования сущностей.

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

Источник: https://habr.com/ru/post/539512/


Интересные статьи

Интересные статьи

(Корень всех зол в data engineering лежит в излишне сложном конвейере обработки данных) Исторический контекст Разработка конвейера данных достаточно серьезная задача, а с учетом обл...
Каждый лишний элемент на сайте — это кнопка «Не купить», каждая непонятность или трудность, с которой сталкивается клиент — это крестик, закрывающий в браузере вкладку с вашим интернет-магазином.
Эта статья для тех, кто собирается открыть интернет-магазин, но еще рассматривает варианты и думает по какому пути пойти, заказать разработку магазина в студии, у фрилансера или выбрать облачный серви...
На сегодняшний день у сервиса «Битрикс24» нет сотен гигабит трафика, нет огромного парка серверов (хотя и существующих, конечно, немало). Но для многих клиентов он является основным инструментом ...
Зайдя на официальный сайт языка программирования Julia, можно увидеть утверждение: "Julia is fast!". Однако, новые пользователи на практике сталкиваются с проблемой медленной загрузки модулей, в ...