Фабричный шаблон, реализующий динамический список фабрик - PullRequest
2 голосов
/ 04 мая 2011

Я реализую абстрактный шаблон фабрики (в c ++), но есть небольшая проблема.

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

Обычно в примерах я вижу что-то вроде этого.

Factory * getFactory()
{
    if(/*we should make factoryA*/)
    {
        return FactoryA::instance();
    }
    else if(/*we should return FactoryB*/)
    {
        return FactoryB::instance();
    }
    else
    {
        return NULL;
    }
}

Я мог бы сделать что-то подобное, но я хочу лучше!

Я имею в виду, что Фабрикабазовый класс будет иметь список фабрик, каждый класс, унаследованный от Factory , создаст статический экземпляр и добавит этот экземпляр в список через функцию защищенного класса в Factory .

Однако я не могу придумать, как это сделать, не играя в русскую рулетку с инициализацией статического объекта.

Ответы [ 4 ]

3 голосов
/ 04 мая 2011

Чтобы избежать проблем со статическим порядком инициализации, вы можете сделать список статическим членом функции getFactoryList (). Это тогда гарантирует, что список существует, когда защищенному конструктору нужно добавить фабрику в список.

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

1 голос
/ 04 мая 2011

Что-то простое:

class BaseFactory
{
    public:
        BaseFactory()
        {
           std::list<BaseFactory*>&  fList = getFactoryList();
           fList.push_back(this);

           // If you are feeling brave.
           // Write the destructor to remove the object from the list.
           //
           // Note C++ guarantees the list will live longer than any of the factories
           // Because the list will always be completely constructed before any
           // of the factory objects (because we get the list in the constructor).
        }

        static BaseFactory& getFactory()  // Don't return a pointer (use a reference)
        {
           std::list<BaseFactory*>&  fList = getFactoryList();
           std::list<BaseFactory*>::iterator i = selectFactory(fList);

           if (i == fList.end()
           {
               static FakeFactory  fakeFactory; // Having a fake factory that
                                                // that returns fake object
                                                // is usually a lot easier than checking for
                                                // NULL factory objects everywhere in the code
                                                //
                                                // Alternatively throw an exception.
               return fakeFactory;
           }

           return *(*i); // return reference
        }
    private:
        static std::list<BaseFactory*>& getFactoryList()
        {
            static std::list<BaseFactory*>  factoryList; // Notice the static
            return factoryList;
        }
};
0 голосов
/ 04 мая 2011

Я использую шаблон для сбора экземпляров подклассов. Вот его голые кости в терминах фабрики:

class Factory {
    public:
      virtual Foo* makeFoo()=0;
      ...
    protected:
      Factory(){
          getFactoryList().push_back(this);
      }
    private:
      FactoryList& getFactoryList();  // Returns static list
};

class FactoryA: public Factory{
      Foo* makeFoo();  // I make a Foo my way
} FACTORYINSTANCE;

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

0 голосов
/ 04 мая 2011

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

Попробуйте Инверсия контроля шаблон вместо.

Если классу A нужно создать объект, передайте фабрику этому классу.

class A {
    Factory *f;
 public:
    A(Factory *f)
       : f(f)
    {  }

    void doSomething()
    {
       Object o = f->produce();
        ...
    }
}

Тогда вы решите, какую фабрику использовать на «более высоком уровне». Фабрика может появиться из исходного кода, из плагина и т. Д.

...