Class fooCreator : public abstractFooCreator 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Class fooCreator : public abstractFooCreator



{

public:

    virtual Foo * create() const { return new C(); }

};

Теперь уже можно написать шаблонный метод для добавления класса в фабрику.

Единственное, что нам нужно уже выбрать контейнер для хранения наших creator'ов.

Выбором может быть std::map.

 

 

typedef std::map<std::string, abstractFooCreator*> FactoryMap;

FactoryMap _factory;

template <class C>

void add(const std::string & id)

{

    typename FactoryMap::iterator it = _factory.find(id);

    if (it == _factory.end())

              _factory[id] = new fooCreator<C>();

}

Появился первый рабочий вариант фабрики, в которую можно добавлять классы, поддерживающие интерфейс Foo.


     Создание объектов нужного типа, практически уже решена, так как по сути она зависела только от того каким образом мы будем хранить creator'ы.

 

  Foo * create(const std::string & id) const

{

    typename FactoryMap::iterator it = _factory.find(id);

    if (it!= _factory.end())

              return it->second->create();

    return 0;

}

Соберем весь код, чтобы увидеть полную картину.

 

Class abstractFooCreator

{

Public:

virtual fooCreator() {}

    virtual Foo * create() const = 0;

};

template <class C>

Class fooCreator: public abstractFooCreator

{

public:

   

    virtual Foo * create() const { return new C(); }

};

С lass FooFactory

{

protected:

    typedef std::map<std::string, abstractFooCreator*> FactoryMap;

    FactoryMap _factory;

public:

    FooFactory();

    ~virtual FooFactory();

    template <class C>

    void add(const std::string & id)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it == _factory.end())

                       _factory[id] = new fooCreator<C>();

    }

    Foo * create(const std::string & id)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it!= _factory.end())

                       return it->second->create();

              return NULL;

    }

};

Пример, как данную фабрику использовать. Добавление классов в фабрику:

FooFactory factory;

factory.add<MyFoo>("MyFoo");

factory.add<MyFoo2>("MyFoo2");

factory.add<ImprovedFoo>("ImprovedFoo");

Создание объекта с помощью фабрики:

Foo * p = factory.create("MyFoo2");

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

 

Создание шаблонной фабрики

 

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

 

Что нужно нашей фабрике, чтобы удовлетворять наши возможности? Конечно, это задание типа идентификатора, который можем быть как и enum'ом, так и std::string или любым другим типом подходящим для идентификатора.

Второе — это собственно тип объектов, которые мы будем создавать, роль которого в примере выше исполнял класс Foo.

 

template <class Base, class IdType>

Class ObjectFactory

{

public:

    ObjectFactory() {}

    virtual ~ObjectFactory();

    template <class C>

    void add(const IdType & id);

    Base * create() const;

};

 

Вот так будет выглядеть наша шаблонная фабрика.

Приступим к ее реализации.

Как и в примере с Foo, нам нужно решить проблему создания объектов конкретного типа.

То есть нам нужен абстрактный класс, у которого есть метод для создания объекта типа Base, и конкретный creator класс, который наследует этот абстрактный класс и реализует этот метод, используя шаблонный параметр.

 

template <class Base>

Class AbstractCreator

{

public:

    AbstractCreator() {}

    virtual ~AbstractCreator(){}

    virtual Base* create() const = 0;

};

template <class C, class Base>

class Creator: public AbstractCreator<Base>

{

public:

    Creator() { }

    virtual ~Creator() {}

    Base * create() const { return new C(); }

};

Ну, а теперь нужно лишь использовать эти классы в нашей фабрике.

 

template <class Base, class IdType>

Class ObjectFactory

{

protected:

    typedef AbstractCreator<Base> AbstractFactory;

    typedef std::map<IdType, AbstractFactory*> FactoryMap;

    FactoryMap    _factory;

public:

    ObjectFactory() {}

    virtual ~ObjectFactory();

    template <class C>

    void add(const IdType & id)

    {

              registerClass(id, new Creator<C, Base>());

    }

protected:

    void registerClass(const IdType & id, AbstractFactory * p)

    {

              typename FactoryMap::iterator it = _factory.find(id);

              if (it == _factory.end())

                       _factory[id] = p;

              else

                       delete p;

    }

};

 

Что существенного добавилось по сравнению с примером Foo?

Конечно же это метод registerClass, который в качестве параметра принимает объект типа AbstractFactory, который, в свою очередь, умеет создавать объекты того класса, который мы указываем как шаблонный параметр для метода add.

 

Добавляем возможность задания поведения фабрики

 

Чтобы уметь задавать поведение фабрики нам нужно использовать другой паттерн проектирования — Стратегия, так же известного под именем Политика.

Данный паттерн очень хорошо освещен в книге                

А. Александреску «Современное проектирование на С++».

 

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

Теперь встроим нашу политику в фабрику. Это можно сделать несколькими способами — просто наследоваться от класса политики, или использовать класс политики как атрибут фабрики.

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

В этом нам поможет замечательная особенность С++ — шаблонные параметры. Когда при объявлении класса фабрики указываем что мы хотим передать в качестве параметра не просто какой-то класс, а шаблонный класс.

Но все это очень трудно.



Поделиться:


Последнее изменение этой страницы: 2021-11-27; просмотров: 46; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 13.58.247.31 (0.016 с.)