2009-06-06 5 views
2

Я ищу решение проблемы дизайна класса C++. То, что я пытаюсь достичь, - это статический метод метода в базовом классе, который будет возвращать экземпляры объектов типов потомков. Дело в том, что некоторые из них должны быть одиночными. Я пишу его в VCL, поэтому есть возможность использовать __properties, но я бы предпочел использовать чистые решения на C++.Как объявить заводской метод в базовом классе?

class Base { 
    private: 
    static Base *Instance; 
    public: 
    static Base *New(void); 
    virtual bool isSingleton(void) = 0; 
} 
Base::Instance = NULL; 

class First : public Base { // singleton descendant 
    public: 
    bool isSingleton(void) { return true; } 
} 

class Second : public Base { // normal descendant 
    public: 
    bool isSingleton(void) { return false; } 
} 

Base *Base::New(void) { 
    if (isSingleton()) 
    if (Instance != NULL) 
     return Instance = new /* descendant constructor */; 
    else 
     return Instance; 
    else 
    return new /* descendant constructor */; 
} 

Возникающие проблемы:

  • как объявить статическую переменную Instance, поэтому было бы быть статическими в классах-потомках
  • как называть потомков конструкторами в базовом классе

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


Edit: некоторые незначительные изменения в коде. Я пропустил несколько указателей.

+0

Вы имеете в виду 'Instance == NULL'? – rriemann

ответ

4

Просто, чтобы проверить нас есть терминологий синхронно - в моей книге, фабрика классов является класс экземпляры которого можно создавать экземпляры некоторого другого класса или классов. Выбор типа создаваемого экземпляра основывается на вводе, который получает фабрика, или, по крайней мере, на том, что он может проверить. Heres очень простой завод:

class A { ~virtual A() {} }; 
class B : public A {}; 
class C : public A {}; 

class AFactory { 
    public: 
     A * Make(char c) { 
     if (c == 'B') { 
      return new B; 
     } 
     else if (c == 'C') { 
      return new C; 
     } 
     else { 
      throw "bad type"; 
     } 
     } 
}; 

Если бы я тебя, я бы снова начать, принимая этот пример, и в виде следующего:

  • factorioes не должен быть одиночками
  • заводов не должны быть статическими членами
  • Заводы не обязательно должны быть членами базового класса для создаваемых ими иерархий
  • Обычно методы фабрики возвращают динамически созданный объект
  • фабричных методов обычно возвращает указатель
  • фабричных методам нужен способ решить, какой класс, чтобы создать экземпляр

Я не вижу, почему ваш завод нуждается в отражении, которое C++ не в любом случае поддержка значимым образом.

+0

Используя типичный завод, как и тот, который вы предоставили, мне придется добавить код в метод Make(), когда я добавлю новый класс потомков. Это то, что я считаю излишним и вредным, поскольку я хочу предоставить свой базовый класс в качестве закрытой структуры. – samuil

+3

Нет, нет. Вы можете зарегистрировать новые классы на заводе без изменения заводского кода. Это очень распространенный пример - прочитали ли вы хорошую книгу по этой теме? – 2009-06-06 08:55:15

+0

@samuil: рассмотрите возможность использования абстрактной фабрики, на которой вы регистрируете конкретные заводы во время выполнения. Вы заказываете свою абстрактную фабрику один раз (это позволяет регистрировать конкретные заводы), и вы создаете новую бетонную фабрику для каждого нового типа, который вы хотите создать абстрактную фабрику. Это довольно распространенная картина. –

0

Как о чем-то вроде этого:

class Base 
{ 
public: 
virtual Base construct() = 0; 
}; 

class First : public Base 
{ 
public: 
Base construct(){ return First(); // or whatever constructor } 
}; 

class Second : public Base 
{ 
public: 
Base construct(){ return Second(...); // constructor } 
}; 
+0

Я использую этот метод прямо сейчас, но я должен написать метод construct() у каждого потомка - я считаю его излишним, так как очевидно, что он должен делать. – samuil

+0

Я думал, что обычной рекомендацией было называть ваши «фабричные» методы 'create()' видящимися, поскольку они очень специфические _not_ конструкторы. –

2

На основании этого на ответ по @Shakedown, я сделаю Base быть шаблонным от фактического типа, используя CRTP:

template <class T> 
class Base 
{ 
public: 
static std::auto_ptr<Base<T> > construct() 
{ 
    return new T(); 
} 
}; 

class First : public Base<First> 
{ 
}; 

class Second : public Base<Second> 
{ 
}; 

Это хорошо, потому что construct теперь снова статический член. Вы назвали бы это нравится:

std::auto_ptr<First> first(First::construct()); 
std::auto_ptr<Second> second(Second::construct()); 

// do something with first and second... 
+0

Интересный подход. В настоящее время я пытаюсь понять, соответствует ли оно моим потребностям и заменяет типичный заводский шаблон, упомянутый Нилом Баттерворт. – samuil

1

Вы можете создать класс Singleton и класс неодноточечных, и сделать все потомки наследуют одну из них.

class Base { 
    public: 
    static Base *New() = 0; 
} 

class SingletonDescendant: public Base { 
    public: 
    *Base::New() { 
     if (Instance != NULL) 
     return Instance = new /* descendant constructor */; 
     else 
     return Instance; 
    } 
    private: 
    static Base *Instance; 
} 
SingletonDescendant::Instance = NULL; 

class NonSingletonDescendant: public Base { 
    public: 
    *Base::New() { 
     return new; 
    } 
} 

class First : public SingletonDescendant{ // singleton descendant 
} 

class Second : public NonSingletonDescendant{ // normal descendant 
} 

Он решает вопросы, которые вы подняли:

  • Как объявить статический переменный экземпляр, так что было бы статичной в классах-потомках: Он существует только в SingletonDescendant класса.
  • Как назвать потомок конструкторов в базовом классе: Использованием Новой функции
  • Я должен написать construct() метод в каждом потомке; Я считаю это излишним, так как очевидно, что он должен делать: Теперь это только в SingletonDescendant и NonSingletonDescendant.
+0

Различие между однопользовательскими и неэлементными потомками выглядит хорошо, но вы оставили свой комментарий/* создатель потомства * /. Из-за этого мне все равно придется реализовать новый метод для каждого потомка. – samuil

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