Возможные реализации для решения конкретных задач на С++

Factory method

Фабричный метод с созданием нового объекта

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

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

class Car
{
public:
    virtual ~Car() = default;  
    virtual void drive() = 0;
};

class Sedan : public Car
{
public:
    Sedan() 
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};
# include <iostream>
# include <memory>

using namespace std;

int main()
{
	shared_ptr<CarCreator> creator = make_shared<ConcreteCarCreator<Sedan>>();
	
	User{}.use(creator);
}

Фабричный метод без повторного создания объектов. Идиома NVI (Non-Virtual Interface).

Задан базовый абстрактный класс CarCreator с public методом getCar(), protected виртуальным методом createCar() и приватным полем car.

Метод getCar() создает Car, если он уже не создан и возвращает поле car.

Неабстрактный класс ConcreteCarCreator наследуется от CarCreator и определяет метод createCar(), для создания конкретного объекта.

Фабричный метод с использованием идиомы NVI применяется в случаях множественного использования тяжелого (тяжело создаваемого) объекта, при отсутствии необходимости в нескольких объектов данного типа.

class Car
{
public:
    virtual ~Car() = default;
    virtual void drive() = 0;
};


class Sedan : public Car
{
public:
    Sedan() 
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};
# include <iostream>
# include <memory>

using namespace std;

int main()
{
    shared_ptr<CarCreator> creator = make_shared<ConcreteCarCreator<Sedan>>();

    unique_ptr<User> user = make_unique<User>();
    user->use(creator);

    return 0;
}

Фабричный метод с шаблонным CarCreator

В данном случае задан один единственный CarCreator, он является шаблонным, что позволяет избавиться от необходимости создания конкретных креаторов (Creator) для каждого типа объекта.

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

class Car
{
public:
    virtual ~Car() = default;
    virtual void drive() = 0;
};


class Sedan : public Car
{
public:
    Sedan() 
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};
# include <iostream>
# include <memory>

using namespace std;

int main()
{
	using SedanCreator_t = CarCreator<Sedan>;
  	shared_ptr<SedanCreator_t> sedanCreator = make_shared<SedanCreator_t>();

	unique_ptr<User> user = make_unique<User>();
	
	user->use(sedanCreator);
}

Фабричный метод со статическим шаблонным методом create

class Car
{
public:
    virtual ~Car() = default;
    virtual void drive() = 0;
};


class Sedan : public Car
{
private:
	int seats;
	double weight;
	
public:
    Sedan(int s, double w) : seats(s), weight(w)
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};
# include <iostream>
# include <memory>
# include <concepts>

using namespace std;

int main()
{
	unique_ptr<User> us = make_unique<User>();

	us->use<Sedan>(1, 100.);
}

Фабричный метод с шаблонным базовым классом Creator

BaseCreator - абстрактный базовый класс создаваемых объектов продуктов (в нашем случае Car), от которого наследуется конкретный шаблонный Creator. Параметр шаблона Tbase - это абстрактный базовый класс иерархии классов продуктов.

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

class Car
{
public:
    virtual ~Car() = default;
    virtual void drive() = 0;
};


class Sedan : public Car
{
private:
	int seats;
	double weight;
	
public:
    Sedan(int s, double w) : seats(s), weight(w)
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};


class SUV : public Car 
{
public:
    SUV(int s, double w) 
    {
        cout << "Calling the SUV constructor;" << endl;
    }
    
    ~SUV() override 
    { 
        cout << "Calling the SUV destructor;" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving SUV;" << endl; 
    }
};
# include <iostream>
# include <memory>

using namespace std;

using SedanCreator_t = Creator<Car, Sedan, int, double>;

int main()
{
    shared_ptr<BaseCarCreator_t> creator = make_shared<SedanCreator_t>();
    unique_ptr<User> user = make_unique<User>();
    user->use(creator);
}

Фабричный метод и "Cтатический полиморфизм" (CRTP)

Статический полиморфизм (compile-time polymorphism) - это механизм, который позволяет вызывать различные функции или методы с одним и тем же именем, но с разными параметрами или типами данных. Это достигается с помощью перегрузки функций или методов. Компилятор статически выбирает соответствующую функцию или метод на основе типов параметров, указанных при вызове.

Задан базовый шаблонный класс Creator, который имеет метод create(). В методе происходит приведение объекта класса к типу, задаваемым параметром шаблона Tcrt, и вызывается метод по созданию объекта (create_impl). Любой креатор, который будет использован для создания базового Creator должен иметь данную функцию по созданию конкретного объекта.

Подход дает большую гибкость для расширения типов креаторов, не меняя написанного кода. Также содержит все плюсы и минусы шаблонных классов.

class Car
{
public:
    virtual ~Car() = default;
    virtual void drive() = 0;
};


class Sedan : public Car
{
public:
    Sedan() 
    { 
        cout << "Sedan constructor called" << endl; 
    }
    
    ~Sedan() override 
    { 
        cout << "Sedan destructor called" << endl; 
    }

    void drive() override 
    { 
        cout << "Driving sedan" << endl; 
    }
};
# include <iostream>
# include <memory>

using namespace std;

int main()
{
    Creator<CarCreator<Sedan>> creator;
    User{}.use(creator);
}

Фабричный метод с использованием идиомы «стирание типа» (Type erasure)

template <typename Derived, typename Base>
concept Derivative = is_abstract_v<Base> && is_base_of_v<Base, Derived>;

template <typename Type>
concept NotAbstract = !is_abstract_v<Type>;
# include <iostream>
# include <memory>
# include <concepts>

using namespace std;

int main()
{
	shared_ptr<CarCreator> creator = make_shared<CarCreator>(Type2Type<Sedan>());

	unique_ptr<User> user = make_unique<User>();

	user->use(creator);
}

Использование паттерна «фабричный метод» для паттерна Command

class BaseCommandCreator
{
public:
    ~BaseCommandCreator() = default;

    virtual shared_ptr<Command> create_command() const = 0;
};


template <typename Tder, typename Tbase = Command>
concept Derived = is_base_of_v<Tbase, Tder>;


template <Derived<Command> Type>
class CommandCreator : public BaseCommandCreator
{
public:
    template <typename... Args>
    CommandCreator(Args ...args)
    {
        create_func = [args...]() { return make_shared<Type>(args...); };
    }
    ~CommandCreator() = default;

    shared_ptr<Command> create_command() const override
    {
        return create_func();
    }
private:
    function<shared_ptr<Command>()> create_func;
};
# include <iostream>
# include <functional>

using namespace std;

int main()
{
    shared_ptr<Invoker> inv = make_shared<Invoker>();
    shared_ptr<Object> obj = make_shared<Object>();

    shared_ptr<BaseCommandCreator> cr = make_shared<SimpleComCreator<Object>>(obj, &Object::operation);

    shared_ptr<Command> com = cr->create_command();

    inv->run(com);
}

Last updated

Was this helpful?