Практическая задачка по С++: фабрика классов (на базе фабрики Александреску)

Модератор: Модераторы разделов

Аватара пользователя
oav
Бывший модератор
Сообщения: 296

Практическая задачка по С++: фабрика классов

Сообщение oav »

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

Сам паттер фабрики и другие описан в http://rsdn.ru/res/book/oo/design_patterns.xml
В кратце задача:

Иметь способ в run-time создавать конкретный тип класса определенной иерархии по некому индентификатору. Идентификатор может быть чем угодно - строка, число, другой класс. Все что угодно что можно сравнивать (есть operator == и operator <).

Использование примерно такое:

BaseClass* pConcreteClass = someFactoryObject.createConcreteClass( "Derived" );
Сама фабрика отлично разжована в книжке Александреску Modern C++ Design.

Вот ее исходный код ( в моем исполнении, но сама идея полностью та):

Код: Выделить всё

//AbstractProduct - is the base class of the hierarchy for which you provide the object factory
//IdentifierType - is the type of the "cookie" that represents a type in the hierarchy. It has to be
//        an ordered type (able to be stored in a std::map). Commonly used identifier types are strings
//        and integral types.
//ProductCreator is the callable entity that creates objects. This type must support
//        operator(), taking no parameters and returning a pointer to AbstractProduct. A
//        ProductCreator object is always registered together with a type identifier.
//FactoryBase is the base class for factory. Must implement a method post_create for post creation object processing.
//        see default_base

template<    typename AbstractProduct ,
            typename IdentifierType,
            typename ProductCreator = AbstractProduct*(*)()
class generic_factory
{
    typedef std::map< IdentifierType , ProductCreator >                creators_t;

    creators_t    creators_;
    generic_factory(void){}
    generic_factory(generic_factory&);
    generic_factory& operator=(const generic_factory&);
public:
    typedef typename IdentifierType                                            identifier_t;
    typedef    typename boost::remove_pointer<AbstractProduct>::type            product_t;
    typedef typename boost::call_traits< IdentifierType >::param_type        identifier_param_t;
    typedef typename boost::add_pointer<product_t>::type                    product_pnt_t;

    //singleton pattern
    static generic_factory& instance()
    {
        static generic_factory factory_;
        return factory_;
    }

    //!perform class registration, return true if class succssesfully registered
    //! \param class_id identifier of class
    //! \param creator function to create class
    bool register_class( identifier_param_t class_id , ProductCreator creator )
    {
        return creators_.insert( std::make_pair(class_id , creator) ).second;
    }

    //!create the class by id. return pointer to the base class
    //! \param class_id class identifier
    //! \return pointer to created class, 0 if identifier is invalid
    product_pnt_t create( identifier_param_t class_id )
    {
        creators_t::const_iterator it = creators_.find( class_id );
        if( it != creators_.end())
        {
            product_pnt_t pnt = (it->second)();
            return pnt;
        }
        return 0;
    }
};


Итак, у нас есть фабрика-синглетон и есть два метода у нее - register_class & create.
Первый "регистрирует" класс, т.е. связывает идентификатор с конкретным ProductCreator'ом. В случае по-умолчанию, ProductCreator - это обычная С-шная функция которая просто делает new на конкретный тип.

Метод create непосредтсвенно ищет по идентификатору конкретный Creator и вызывает его - все просто как тапочки.

Пример использования всего этого хозяйства:

Код: Выделить всё

struct Base
{
...
};

struct Der1: public Base
{
....
};

struct Der2: public Base
{
....
};

typedef enum BaseID{ idDer1,idDer2 };
typedef generic_factory< Base , BaseID > MyFactory;

в .cpp файле:

Base* createDir1()
{
      return new Dir1;
}

Base* createDir2()
{
      return new Dir2;
}

где-то еще в коде приложения:

MyFactory::instance().register_class( idDer1 , &createDir1 );
MyFactory::instance().register_class( idDer2 , &createDir2 );


Как видим выходит много избыточного кода, который по сути одинаковый + который надо не забывать вызывать в правельное время (т.е. register должен быть раньше create'a) и другие проблемы, например избыточность явная.

Упростить это и есть задача :)

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

Условия:
1) Сделать механизм автоматической регистрации классов. Т.е. убрать необходимость думать и месте и времени вызова register_class

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

3) Выполнить это типо-безопасно.

4) _НЕ_ использовать макросы С.

В идеале решение должно быть такое:

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

Если ответов совсем не будет, опубликую ответ быстро, если завяжеться дискуссия (конструктивная), то подожду :)
Спасибо сказали:
Аватара пользователя
flook
Сообщения: 585
Статус: Просто flook

Re: Практическая задачка по С++: фабрика классов

Сообщение flook »

oav писал(а):
24.03.2006 13:47
Условия:
1) Сделать механизм автоматической регистрации классов. Т.е. убрать необходимость думать ...

No comments...

oav писал(а):
24.03.2006 13:47
2) В _нашем_ приложении оказалось много иерархий классов, которые сами по себе маленькие, но их количество весьма велико. На каждый писать Creator& вызов register_class - ужасная трата времени и большой объем исходников.

А условие то где?

oav писал(а):
24.03.2006 13:47
4) _НЕ_ использовать макросы С.

Ну, это вы, батенька, зря. При грамотном подходе макросами можно очень много чего сделать такого, чего без них - ну ни как.
В каждом из нас спит гений... и с каждым днем все крепче...
Спасибо сказали:
Аватара пользователя
oav
Бывший модератор
Сообщения: 296

Re: Практическая задачка по С++: фабрика классов

Сообщение oav »

[quote]1) Сделать механизм автоматической регистрации классов. Т.е. убрать необходимость думать ...[quote]
Не понял мысль.

[quote
2) В _нашем_ приложении оказалось много иерархий классов, которые сами по себе маленькие, но их количество весьма велико. На каждый писать Creator& вызов register_class - ужасная трата времени и большой объем исходников.

А условие то где? [quote]
Как где? Еслибы было маленькое количтсво классов и все они были бы толстыми, то скопом их вводить в одну область видимости - очень дорогое удовольствие и авто регистрацию целого списка делать не нужно, лучше каждый класс сам себя зарегерит. Ниже приведу пример (тут, как раз лучше всего пользовать макрос).

[quote]
4) _НЕ_ использовать макросы С.
Ну, это вы, батенька, зря. При грамотном подходе макросами можно очень много чего сделать такого, чего без них - ну ни как.
[quote]
Я не утверждал что макросы зло. Просто эту задачу на них не решить нормально, по этому считаю нужным отрубить этот путь обдумывания сразу.

А вот макрос, который удобно использовать если (см. выше). Автоматичекая регистрация одного класса. Ее необходимо вставить в .срр файл класса.

Код: Выделить всё

//!Simple macros for auto class registration during initializing static variables
//!Not working if class type dublicates in single compilation unit
#define REGISTER_CLASS( factoryName , className , classType)namespace{\
    factoryName::product_pnt_t create##classType(){\
    return new classType();}\
    bool is##classType = factoryName::instance().register_class( className , create##classType );}


Есть и минус, не работает если в .срр файле есть несколько регистраций одного класса на разные ID (у нас таких случаев оказалось много)

Регистрация произойдет на этапе инициализации C++ run-time.

Задача реально интересно и точно не такая-уж простая как кажеться - мне лично очень понравилось ее решать в свое время по-этому и хочеться поделиться. Сразу скажу что исходного кода там очень мало и все сосредоточено в паре структурок. Все обсолютно С++-стандратно и будет работать на любом компиляторе современном. Еще правда используется буст, но это чтобы не изобретать велосипед. Loki тоже было бы достаточно
Спасибо сказали:
Аватара пользователя
oav
Бывший модератор
Сообщения: 296

Re: Практическая задачка по С++: фабрика классов

Сообщение oav »

Совсем никаких идей ни у кого?:unsure:
Спасибо сказали:
andigor
Сообщения: 1

Re: Практическая задачка по С++: фабрика классов

Сообщение andigor »

Ну на сам ответ то где? :mellow:
Спасибо сказали:
Аватара пользователя
Alxn1
Сообщения: 402
Статус: Красноглазик со стажем
ОС: Mavericks

Re: Практическая задачка по С++: фабрика классов

Сообщение Alxn1 »

Код: Выделить всё

template< class Class, ClassIDType classId, FactoryClass factory >
class ClassRegistrator
{
    public:
        static Class *createClassInstance()
        {
            return new Class();
        }

        ClassRegistrator()
        {
            factory::instance().register_class( classId , &createClassInstance );
        }
};

class SomeClass
{
    public:
        ...

    private:
        static ClassRegistrator< SomeClass, myId, myFactory > sRegistrator;
};

ClassRegistrator< SomeClass, myId, myFactory > SomeClass::sRegistrator;


Я бы вот так вот сделал. Правда, тут есть подводные камни в виде статических обьектов, который хрен знает в каком порядке будут проинициализированны, и будут ли вообще, зато без макросов (хотя, и с макросами беды были бы те же). Это похоже на ваше решение проблемы?
Спасибо сказали: