Уместен ли здесь фабричный метод?

Я генерирую последовательность Step объектов, которые отличаются "Типом" и данными, содержащимися внутри. например:

Объекты Step в основном должны быть структурами, которые выглядят так:

{ GRAB, CASCADE_ONE, FACEUP, SOMEOTHERDATA },
{ DROP, DECK,  FACEDOWN, MOREDATA, ANDSOMEMORE },
{ MOVE, 34, 89 },

где GRAB, MOVE и DROP обозначают StepType:

typedef enum
{
     GRAB,
     DROP,
     MOVE
}StepType;

Как видите, в зависимости от StepType каждая из этих структур имеет переменное количество полей данных после StepType.

Я планирую перебрать последовательность этих структур и выполнить определенное действие на основе поля StepType. Моя первая догадка заключается в том, что это должны быть объекты классов, производных от абстрактного класса Step, т.е. я должен создать класс GrabStep, класс MoveStep и класс DropStep.

Хороший ли это дизайн, и если да, то должен ли я создавать их, используя фабричный метод? Если использовать фабричный метод, то как инициализировать поля внутри объектов?


person BeeBand    schedule 12.07.2010    source источник
comment
Я не очень понимаю, какое отношение фабричные методы имеют к вашей проблеме.   -  person ereOn    schedule 12.07.2010


Ответы (3)


Для этого вам не нужен заводской шаблон. Но создание абстрактного класса Step — хорошее начало:

class Step
{
private:
    // The presence of a pure virtual makes this class abstract.
    virtual void DoAction() = 0;
public:
    virtual ~Step() {} // Needed if you are going to delete via a Step* pointer
    void Action() { DoAction(); } // Template method pattern
};

// All other classes derive publicly from Step, since they all have an "is-a"
// relationship with Step (i.e. a GrabStep "is-a" Step).
class GrabStep : public Step
{
private:
    void DoAction() { /* Do whatever a GrabStep does */ };
    // Data relevant to GrabStep
};

class MoveStep : public Step
{
private:
    void DoAction() { /* Do whatever a MoveStep does */ };
    // Data relevant to MoveStep
};

class DropStep : public Step
{
private:
    void DoAction() { /* Do whatever a DropStep does */ };
    // Data relevant to DropStep
};

Затем вы можете перебирать эти вещи, не зная их точных типов:

// Example:
std::vector<Step*> seq; // or some other container
// Note that we are storing Step* pointers in a container instead of Step
// objects. This is needed for polymorphism to work.
// ...
seq.push_back(new GrabStep);
seq.push_back(new MoveStep);
seq.push_back(new DropStep);
// ...
for(std::vector<Step*>::iterator i = seq.begin(); i != seq.end(); ++i)
{
    // Will call the proper version of DoAction() depending on the actual type.
    (*i)->Action();
}
// ...
// After we are done, clean up after ourselves. This is needed because
// std::vector does not delete the pointees.
for(std::vector<Step*>::iterator i = seq.begin(); i != seq.end(); ++i)
{
    delete (*i); // Safe because Step has a virtual destructor.
}
person In silico    schedule 12.07.2010
comment
Step обычно нужен виртуальный деструктор. - person Philipp; 12.07.2010
comment
@In silico - как классы DropStep, GrabStep должны наследоваться от Step- частным или публичным образом? - person BeeBand; 12.07.2010
comment
@In silico - я не могу скомпилировать Step* i, который у вас есть в цикле for. Это должен быть итератор? - person BeeBand; 12.07.2010
comment
@BeeBand — наследование public, поскольку DropStep, GrabStep и MoveStep имеют отношения is-a с Step. Кроме того, да, вы должны использовать итератор. - person In silico; 13.07.2010
comment
+1 - хороший ответ, в этом случае полиморфизм - правильное решение. - person Matthew Iselin; 13.07.2010

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

Фабричный шаблон - это когда вызывающая сторона не заботится о типе объекта, который им понадобится, только о том, что он соответствует интерфейсу. Например, синтаксический анализатор файла конфигурации, которому все равно, где находится файл конфигурации, является ли он двоичным или xml. Все, что хочет делать синтаксический анализатор, — это читать элементы.

В вашем случае любой код, выбирающий тип шага для построения, по определению заботится о том, какой тип объекта они создают. Вы по-прежнему можете использовать фабричный шаблон для абстрагирования конструкции, но вам понадобится одна фабричная функция (с соответствующими параметрами) для каждого типа шага.

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

person tenpn    schedule 12.07.2010

Если вам нужен полиморфизм во время выполнения, то я думаю, что это хороший дизайн. По поводу фабричной функции: если она нужна вашим клиентам, то напишите. Фабричная функция просто вызывает конструкторы подклассов.

person Philipp    schedule 12.07.2010