Разве шаблон фабрики - это не то же самое, что глобальное состояние? - PullRequest
4 голосов
/ 05 марта 2009

Допустим, у меня есть такой класс:

class MonkeyFish
{
   MonkeyFish( GlobalObjectA & a, GlobalObjectB & b, GlobalObjectC & c);

   private:
     GlobalObjectA & m_a;
     GlobalObjectB & m_b;
     GlobalObjectC & m_c;
}

Без фабрики мне нужно сделать следующее, чтобы создать экземпляр MonkeyFish.

GlobalObjectA a;
GlobalObjectB b;
GlobalObjectC c;

int main()
{
  MonkeyFish * monkey_fish = new MonkeyFish(a, b, c);
  monkey_fish->go();
}

С другой стороны, если у меня есть MonkeyFishFactory, похоже, я должен сделать это:

GlobalObjectA a;
GlobalObjectB b;
GlobalObjectC c;

int main()
{
  MonkeyFishFactory mf_factory(a, b, c);
  MonkeyFish * monkey_fish = mf_factory.buildMonkeyFish("Bob");
  monkey_fish->go();
}
  1. У меня все еще есть глобальные объекты.

  2. Даже если сама MonkeyFishFactory создала GlobalObjects внутренне (поэтому они теперь находятся внутри MonkeyFishFactory вместо истинных глобальных переменных), похоже, что MonkeyFishFactory сама все еще должна быть глобальным объектом так что я могу получить к нему доступ в любое время, когда я хочу создать MonkeyFish.

Разве в этом случае шаблон Factory не совпадает с глобальным состоянием?

(В настоящее время я работаю в предположении, что глобальное состояние - это Плохая вещь, а устранение - Хорошая.)

Ответы [ 8 ]

7 голосов
/ 06 марта 2009

Вы здесь путаете понятия?

Шаблон Factory обычно применяется, когда вы возвращаете экземпляр конкретного класса, который скрывается за абстрактным интерфейсом. Идея состоит в том, что вызывающая сторона увидит только интерфейс и даже не должна знать, какой конкретно тип объекта. Это все о создании экземпляра объекта на основе параметров и отделении логики, связанной с решением, какой объект создать от пользователя, создающего объект.

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

class IMonkeyFish {
public:
    virtual ~IMonkeyFish() = 0;
    virtual void go() = 0;
};

class Factory {
public:
    static Factory& instance();
    IMonkeyFish* createMonkeyFish();
protected:
    Factory(GlobalObjectA& a, GlobalObjectB& b, GlobalObjectC& c);
private:
    static Factory *theInstance;
    GlobalObjectA&  instanceOfA;
    GlobalObjectB&  instanceOfB;
    GlobalObjectC&  instanceOfC;
};

Factory& factory = Factory::instance();
IMonkeyFish* fishie = factory.createMonkeyFish();
fishie->go();

Шаблон Singleton управляет созданием фабричного экземпляра. Шаблон Factory скрывает детали, связанные с созданием объектов, которые реализуют интерфейс IMonkeyFish. Хорошая вещь (TM) - это сокрытие глобального состояния и отделение MonkeyFish конкретных деталей от создания экземпляра.

Использование или правильность использования Singleton - это совсем другая проблема. Вероятно, вокруг этого тоже есть куча потоков.

6 голосов
/ 06 марта 2009

Глобальное состояние само по себе не является плохим. Public глобальное состояние - это плохо. Шаблон Factory помогает инкапсулировать глобальное состояние, что является хорошей вещью.

5 голосов
/ 06 марта 2009

На фабрике нет глобального состояния. Он просто создает объекты. Так как это не любое состояние на заводе. Можно быть глобальным.

3 голосов
/ 06 марта 2009

Задача фабричного класса - создать экземпляр объекта и передать его вызывающей стороне; не выбирать какой глобальный экземплярный объект использовать. Итак, ваш фабричный пример неверен. Должно быть:

int main()
{
  MonkeyFish * monkey_fish = MonkeyFishFactory::buildMonkeyFish("Bob");
  monkey_fish->go();
}

Обратите внимание, глобальные объекты отсутствуют, а MonkeyFishFactory не создан.

3 голосов
/ 06 марта 2009

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

3 голосов
/ 06 марта 2009

Вам не нужно оставлять глобальные объекты. Обезьянья рыбная фабрика должна создавать такие GlobalOjectA | B | C по требованию. Используя переключатель или метод внутри, чтобы определить, какой.

1 голос
/ 06 марта 2009

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

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

Постарайтесь четко заявить, чего вы пытаетесь достичь, и вы, вероятно, получите лучший ответ.

1 голос
/ 06 марта 2009

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

...