Когда завод подходит? - PullRequest
       8

Когда завод подходит?

2 голосов
/ 30 ноября 2010

Взято из википедии:

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

Может ли кто-нибудь объяснить, когда фабричный класс необходим или выгоден?

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

Ответы [ 4 ]

7 голосов
/ 30 ноября 2010

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

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

3 голосов
/ 30 ноября 2010

Цель фабрики - определить во время выполнения и, следовательно, потенциально и часто с данными, недоступными во время компиляции программы, которые из многих class всех, полученных из определенного класса с virtual методами, должны захватывать эти данные для вашей программы.

Дело в том, что функции полиморфного языка позволяют работать с различными типами данных (совместно используя общий базовый класс), вызывая поведение, соответствующее типу. Чтобы извлечь выгоду из этого, вам нужно иметь объекты разных производных типов. Когда вы узнаете об этом в курсе Comp Sci, вы, вероятно, жестко создадите несколько из каждого типа Derived и поиграете с ними через указатели на базовый класс. В сложных реальных задачах, а не в жестком кодировании, оно часто вытесняется из данных, поступающих из входных данных программ, таких как таблицы базы данных, файлы и сокеты. В зависимости от того, что именно вы видите в каждой точке, вы хотите создать объект с соответствующей типизацией для его представления, но вам, вероятно, потребуется вести запись об этом с использованием известного типа во время компиляции: указатель на базовый класс. Затем вы можете не только предварительно выполнить операции, которые обещает базовый класс - некоторые из которых могут включать динамическую диспетчеризацию для реализации производного класса, но вы также можете - при необходимости - точно определить, каков реальный тип данных, и соответствующим образом вызвать действия .

Например, скажем, вы прочитали следующий файл, который показывает, как вы собрали различные типы данных для каждого:

elephant name Tip-Toes partner Mega
mule name Dare-You mane_length 132

У вас есть следующая иерархия классов, чтобы представить их:

struct Animal
{
    Animal(const std::string& name) : name_(name) { }

    virtual void eat_from(Supplies&) = 0; // animals must do in their own way...
    virtual bool can_jump() { return false; } // some animals might, assume not...

    std::string name_;
};

struct Elephant : Animal
{
    Elephant(const std::string& name, const std::string& partner)
      : Animal(name), partner_(partner)
    { }

    std::string partner_;

    virtual void eat_from(Supplies&) { supplies.consume(Tofu, 10 * kg); }
    void swing_trunk(); // something specific to elephants
};

struct Mule : Animal
{
    Mule(const std::string& name, double kgs) : Animal(name), kilograms_(kgs) { }
    double kilograms_;

    virtual void eat_from(Supplies&) { supplies.consume(Grass, 2 * kg); }
    virtual bool can_jump() { return true; }
};

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

Animal* factory(std::istringstream& input)
{
    std::string what, name;
    if (input >> what && input >> name)
    {
        if (what == "elephant")
        {
            std::string partner;
            if (input >> partner)
                return new Elephant(name, partner);
        }
        else if (what == "mule")
        {
            double mane_length;
            if (input >> mane_length)
                return new Mule(name, mane_length);
        }
    }
    // can only reach here on unparsable input...
    throw runtime_error("can't parse input");
}

Затем вы можете хранить * Животных и выполнять над ними операции:

std::vector<Animal*> animals;
// we expect 30 animals...
for (int i = 0; i < 30; ++i) animals.push_back(factory(std::cin));

// do things to each animal...
for (int i = 0; i < 30; ++i)
{
    Animal* p_unknown = animals[i];

    std::cout << p_unknown->name() << '\n';

    if (Elephant* p = dynamic_cast<Elephant*>(p_unknown))
        p->swing_trunk();
}

Возвращаясь к вашему вопросу:

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

Нет, не ситуация, когда фабрика полезна, так как задействован только один тип. Просто придерживайтесь того, что у вас есть (в смысле ОО), но вы можете выбрать исключение, отменить программу и т. Д., А не устанавливать флаг, который вызываемый может или не может проверить.

1 голос
/ 30 ноября 2010

попробую простой ответ:)

Из Википедия .

Использовать фабричный шаблон при:

  • Создание объекта исключает повторное использование без значительного дублирования кода.
  • Создание объекта требует доступа к информации или ресурсам, которые не должны содержаться в составляющем объекте.
  • Управление жизненным циклом созданных объектов необходимо централизовать для обеспечения согласованного поведения.

Так что я думаю, вам не нужна фабрика в вашем конкретном случае. Но я бы сказал, что это не повредит.

Обычное использование для фабрик - это если вы хотите вернуть интерфейс и скрыть реализующий класс.

Например:

public final class LoginFactory {

private final static Map<SomeEnum, LoginInterface> IMPLEMENTERS = new HashMap<SomeEnum, LoginInterface>();

static {
    IMPLEMENTERS.put(SomeEnum.QUICK, new QuickerLoginImpl());
    IMPLEMENTERS.put(SomeEnum.SECURE, new SecureLoginImpl());
}

public static LoginInterface getLoginImpl(SomeEnum type) { // my naming is bad ...
    return IMPLEMENTERS.get(type);
}

}

Таким образом, вы можете изменить SecureLoginImpl на MoreSecureLoginImpl, например, даже если пользователь вашего API не заметил.

Возможно, вы захотите взглянуть на эту вики-страницу Абстрактный шаблон фабрики .

1 голос
/ 30 ноября 2010

Мое мнение о шаблоне Factory всегда было несколько иным, но я все равно его приведу.

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

Фабрика, которая создает только один тип объекта (отсутствует «родство»), - это не Фабрика, а скорее Стратегия.

Мои приведенные выше определения подразумевают, что Factory не используется или не нужен так часто, как вы думаете.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...