2011-05-04 6 views
2

Я реализую абстрактный шаблон фабрики (в 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.

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

+0

Что вы пытаетесь достичь? Как вы решаете, какая фабрика должна быть возвращена? Зачем вам нужен список, как бы вы вытащили правильную фабрику из списка? –

+0

Что возвращает FactoryA :: instance()? Возвращает ли он экземпляр фабрики? Возвращает ли он экземпляр нового A? Мой первый взгляд на ваш код заставил меня думать, что функция возвращает фабрики. Если это так, ваш подход, вероятно, неверен. –

+0

@dauphic: Это относительно стандартный шаблон. У вас есть куча фабричных объектов, и вам нужно динамически решать во время выполнения, которое нужно использовать. –

ответ

1

Somthing Простой:

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

Спасибо за хороший ответ. Правильно ли это (?): Статические переменные, объявленные в функциях, гарантированно создаются при вводе функции, даже в том случае, когда функция вводится из инициализации статического объекта? – 0xbaadf00d

+0

Объект полностью построен после завершения его конструктора. Объекты статического хранения (объекты области глобальной привязки/объекты статической функции) сохраняются в том порядке, в котором они созданы (завершение конструктора), и уничтожаются в обратном порядке создания (это все гарантировано стандартом). Порядок глобальных объектов немного туманные, но статические объекты функции инициализируются при первом использовании. Таким образом, если глобальный объект вызывает функцию со статическим объектом из своего конструктора, этот объект будет полностью построен до того, как конструктор глобалов будет завершен и, следовательно, будет построен первым, –

+0

будет 'this' работать , когда 'Factory' имеет множественное наследование, а' BaseFactory' - не первый базовый класс? – q126y

0

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

Попробуйте Inversion Of Control вместо этого.

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

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

+0

Вопрос о выборе фабрики. Вы на шаг впереди, когда вы выбрали завод и передали завод на объект. Прежде чем вы сможете это сделать, вам нужно получить завод. –

3

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

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

0

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

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 просто вычисляет уникальное имя для того, чтобы вызывать его собственный конструктор.

Смежные вопросы