Является ли «фабричный» метод правильным шаблоном? - PullRequest
1 голос
/ 11 марта 2010

Так что я работаю над улучшением существующей реализации. У меня есть ряд полиморфных классов, которые все составлены в контейнерный класс более высокого уровня. Проблема, с которой я сейчас сталкиваюсь, состоит в том, что контейнерный класс более высокого уровня, ну, отстой. Это выглядит примерно так, с чем у меня действительно нет проблем (так как полиморфные классы в контейнере должны быть открытыми). Моя настоящая проблема в конструкторе ...

/*
 * class1 and class 2 derive from the same superclass
 */
class Container 
{
  public: 
   boost::shared_ptr<ComposedClass1> class1;  
   boost::shared_ptr<ComposedClass2> class2;
  private:
   ...
}

/*
 * Constructor - builds the objects that we need in this container. 
 */ 
Container::Container(some params)
{
  class1.reset(new ComposedClass1(...));
  class2.reset(new ComposedClass2(...));
}

Что мне действительно нужно, так это сделать этот класс контейнера более пригодным для повторного использования. Путем жесткого кодирования объектов-членов и создания их экземпляров, в принципе, это не так, и их можно использовать только один раз. Фабрика - это один из способов создания того, что мне нужно (возможно, путем предоставления списка объектов и их конкретных типов, которые будут созданы?). Похоже, кто-то должен был решить это раньше ... Спасибо!

Ответы [ 6 ]

3 голосов
/ 11 марта 2010

Впрыск зависимости приходит на ум.

0 голосов
/ 11 марта 2010

Как правило, я согласен с комментарием Томса: недостаточно информации.

На основании ваших разъяснений:

Это просто контейнер для ряда "компонентов"

Если список компонентов меняется, предоставьте методы Add / Enumerate / Remove для контейнера.

Если список компонентов должен быть фиксирован для каждого экземпляра контейнера, предоставьте объект построителя с методами

Builder.AddComponent
Builder.CreateContainer

Первое позволит повторно использовать контейнер, но часто оказывается более сложным в моем опыте. Второй часто требует способа поменять / заменить контейнеры, но в итоге его легче реализовать.

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

0 голосов
/ 11 марта 2010

На основании вашего описания я бы порекомендовал Абстрактную Фабрику, переданную в конструктор.

class ClassFactory {
public:
   virtual ~ClassFactory
   virtual Class1 * createClass1(...) = 0;  // obviously these are real params not varargs
   virtual Class2 * createClass2(...) = 0;
};

class DefaultClassFactory : public ClassFactory {
    //  ....
};


Container::Container(
    some params, 
    boost::smart_ptr<ClassFactory> factory = boost::smart_ptr<ClassFactory>(new DefaultClassFactory))
{
  class1.reset(factory->createClass1(...));
  class2.reset(factory->createClass1(...));
}

Это форма внедрения зависимости без фреймворка. Обычные клиенты могут использовать DefaultClassFactory прозрачно, как и раньше. Другие клиенты, например, модульные тесты, могут внедрить свою собственную реализацию ClassFactory.

В зависимости от того, как вы хотите контролировать владение параметром factory, это может быть smart_ptr, scoped_ptr или ссылка. Ссылка может привести к немного более чистому синтаксису.

Container::Container(
    some params, 
    ClassFactory & factory = DefaultClassFactory())
{
    class1.reset(factory.createClass1(...));
    class2.reset(factory.createClass1(...));
}

void SomeClient()
{
    Container c(some params, SpecialClassFactory());
    // do special stuff
}
0 голосов
/ 11 марта 2010
public class factory
{
    public static Class1 Create()
      {
            return new Class1();
      }
}

class Class1
{}
main()
{
   Classs1 c=factory.Create();   
}
0 голосов
/ 11 марта 2010

Вы знаете, что у вас может быть второй конструктор, который этого не делает?

Container::Container(some params)
{
    // any other initialization you need to do
}

Затем вы можете установить class1 и class2 по своему усмотрению, поскольку они общедоступны.

Конечно, они не должны быть, но с этим кодом, это то, что вы могли бы сделать.

Насколько я вижу из того, что вы описали, заводская модель не будет такой полезной.

0 голосов
/ 11 марта 2010

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

class1* Container::GetClass1; virtual;
{
  return new ComposedClass1;
}

class1* SpecialContainer::GetClass1;
{
  return new SpecialComposedClass1;
}

Container::Container(some params)
{
  class1.reset(GetClass1);
  class2.reset(GetClass2);
}

Другой подход заключается в отделении создания зависимостей Контейнера от Контейнера. Для начала вы можете изменить конструктор на инъекцию зависимостей class1 и class2.

Container::Container(aclass1, aclass2)
{
  class1.reset(aclass1);
  class2.reset(aclass2);
}
...