шаблон проектирования фабричного метода

По книге:

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

Скажем, у меня есть класс для авторов:

class Product; //this is what the Factory Method should return
class Creator {
    public:
        Creator()   //ctor
        { //... }

        virtual Product make(//args)
        { //... }
}

Хорошо, это мой класс для авторов, но я не понимаю

Метод Factory позволяет классу отложить создание экземпляра до подклассов.

При чем тут подклассы? И для чего мне использовать подклассы?

Кто-нибудь может привести мне пример?


person Alcott    schedule 19.09.2011    source источник


Ответы (8)


Ваш Creator класс - это фабрика. Назовем его ProductFactory, чтобы сделать пример более ясным.

(я предполагаю, что вы используете C ++)

class Book : public Product
{
};

class Computer : public Product
{
};

class ProductFactory
{
public:
  virtual Product* Make(int type)
  {
    switch (type)
    {
      case 0:
        return new Book();
      case 1:
        return new Computer();
        [...]
    }
  }
}

Назовите это так:

ProductFactory factory = ....;
Product* p1 = factory.Make(0); // p1 is a Book*
Product* p2 = factory.Make(1); // p2 is a Computer*
// remember to delete p1 and p2

Итак, чтобы ответить на ваш вопрос:

При чем тут подклассы? И для чего мне использовать подклассы?

Определение шаблона фабрики говорит о том, что фабрика определяет общий API для создания экземпляров определенного типа (обычно интерфейса или абстрактного класса), но реальный тип возвращаемых реализаций (таким образом, ссылка на подкласс) является обязанностью фабрика. В этом примере фабрика возвращает Product экземпляров, для которых Book и Computer являются допустимыми подклассами.

Существуют и другие идиомы для фабрики, например наличие API для фабрики и конкретные реализации фабрики не принимают type, как в моем примере, но они связаны с типом возвращаемых экземпляров, например это:

class ProductFactory
{
public:
  virtual Product* Make() = 0;
}

class BookProductFactory : public ProductFactory
{
public:
    virtual Product* Make()
    {
      return new Book();
    }
}

В этом классе BookProductFactory всегда возвращает Book экземпляров.

ProductFactory* factory = new BookProductFactory();
Product* p1 = factory->Make(); // p1 is a Book
delete p1;
delete factory;

Чтобы прояснить ситуацию, поскольку кажется, что между шаблонами проектирования Abstract Factory и Factory method существует некоторая путаница, давайте посмотрим на конкретный пример:

Использование абстрактной фабрики

class ProductFactory {
protected:
  virtual Product* MakeBook() = 0;
  virtual Product* MakeComputer() = 0;
}

class Store {
public:
   Gift* MakeGift(ProductFactory* factory) {
     Product* p1 = factory->MakeBook();
     Product* p2 = factory->MakeComputer();
     return new Gift(p1, p2);
   }
}

class StoreProductFactory : public ProductFactory {
protected:
  virtual Product* MakeBook() { return new Book(); }
  virtual Product* MakeComputer() { return new Computer(); }
}

class FreeBooksStoreProductFactory : public StoreProductFactory {
protected:
  virtual Product* MakeBook() {
    Book* b = new FreeBook(); // a FreeBook is a Book with price 0
    return b;
  }
}

Это используется так:

Store store;
ProductFactory* factory = new FreeBooksStoreProductFactory();
Gift* gift = factory->MakeGift(factory);
// gift has a FreeBook (Book with price 0) and a Computer
delete gift;
delete factory;

Использование заводского метода

class Store {
public:
   Gift* MakeGift() {
     Product* p1 = MakeBook();
     Product* p2 = MakeComputer();
     return new Gift(p1, p2);
   }

 protected:
   virtual Product* MakeBook() {
     return new Book();
   }

   virtual Product* MakeComputer() {
     return new Computer();
   }
}

class FreeBooksStore : public Store {
protected:
  virtual Product* MakeBook() {
    Book* b = new FreeBook(); // a FreeBook is a Book with price 0
    return b;
  }
}

Это используется так:

Store* store = new FreeBooksStore();
Gift* gift = store->MakeGift();
// gift has a FreeBook (Book with price 0) and a Computer
delete gift;
delete store;

Когда вы используете type дискриминатор, как я использовал в исходном примере, мы используем parametized factory methods - метод, который знает, как создавать различные типы объектов. Но это может быть как Abstract Factory, так и Factory Method. Небольшой трюк: если вы расширяете фабричный класс, вы используете абстрактную фабрику. Если вы расширяете класс методами создания, вы используете фабричные методы.

person Jorge Ferreira    schedule 19.09.2011
comment
Итак, в вашем коде ProductFactory - это абстрактная фабрика, а make - это фабричный метод, верно? - person Alcott; 19.09.2011
comment
Разница между Abstract Factory и Factory Method будет вопросом сама по себе :) Короче говоря, когда вы используете дискриминатор типов, как я в примере, мы используем параметризованные фабричные методы - метод, который знает, как создавать различные типы объектов. Но это может появиться либо в шаблоне абстрактной фабрики, либо в шаблоне фабричного метода. Небольшой трюк: если вы расширяете фабричный класс, вы используете Abstract Factory. Если вы расширяете класс с помощью фабричных методов, вы используете Factory Methods. Если разница все еще неясна, задайте другой вопрос по SO. - person Jorge Ferreira; 19.09.2011
comment
Чтобы было понятно. В моем ответе используется не Factory method шаблон проектирования, а Abstract Factory шаблон проектирования. - person Jorge Ferreira; 19.09.2011

Шаблон Factory просто означает, что существует некоторый класс или метод Factory, который отвечает за создание объектов за вас; вместо того, чтобы создавать их сами. Во многом как машины строят на заводах, так что вам не обязательно.

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

Например, WebRequest.Create ("http://www.example.com") вернет мне HttpWebRequest, но WebRequest .Create ("ftp://www.example.com") вернет мне FtpWebRequest, потому что оба имеют разные протоколы которые реализуются разными классами, но общедоступный интерфейс такой же, поэтому это решение не должно приниматься потребителем моего API.

person Muhammad Hasan Khan    schedule 19.09.2011

Product Make () создаст правильный тип (подкласс) продукта на основе определенных условий и «отложит» фактическое создание конкретных продуктов.

(псевдокод)

public class Product
{
    public static Product Make()
    {
        switch(day_of_week)
        {
           case Monday: return new Honey(1.1);
           case Wednesday: return new Milk(3.6);
           case Thurday: return new Meat(0.5);
           case Friday: return new Vegetable(1.3);
           case Saturday: return new Vegetable(2.3); // more expensive on saturday, only factory need to know
           default: return null; // off day!
        }
    }

    // returns price based on underlying product type and hidden/auto conditions (days of week)
    public virtual void GetPrice() { return Price; }

    // sometimes a factory can accept a product type enum
    // From API POV, this is easier at a glance to know avaliable types.
    pubic enum Type { Milk, Honey, Meat, Vegetable };

    public static Product Make(Type, Day)
    {
        // create the specified type for the specified day.
    }
}

public class Honey : Product { Price = arg; }
public class Milk : Product { Price = arg; }
public class Meat : Product { Price = arg; }
public class Vegetable : Product { Price = arg; }

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

person Jake    schedule 19.09.2011
comment
означает ли это, что фабричный метод может производить (возвращать) несколько продуктов, но какой из них создать внутри фабричного метода, и возврат определяется в соответствии с некоторыми условиями (такими как аргументы, переданные фабричному методу). - person Alcott; 19.09.2011
comment
@Alcott, пожалуйста, посмотрите, что к моему ответу добавлено еще несколько подробностей. - person Jake; 19.09.2011

Просто и кратко:

На фабрике проверяется, какой «подкласс» требуется создать, поэтому "let the subclasses decide which class to instantiate"
(Вы используете условные операторы в фабричном классе, где необходимо принять решение.)

"define an interface or abstract class for creating an object". очевидно, что вы сохраняете объект в ссылке на интерфейс, а клиент не знает, какой объект конкретного класса возвращается. (Итак, вы определили интерфейс для создания объекта).

person Arun Raaj    schedule 25.09.2017

Могу только предположить, что он имеет в виду следующее:

class Product; //this is what the Factory Method should return
class Box : Product;

class Creator {
    public:
        Creator()   //ctor
        { //... }

        virtual Product* make(//args) = 0;
};

class BoxCreator{
    public:
        BoxCreator()
        {}
        virtual Product* make()
        {}
};

Creator* pCreator = new BoxCreator;
Product* pProduct = pCreator->make(); //will create a new box

Однако это не стандартный способ создания фабрики.

person Luchian Grigore    schedule 19.09.2011
comment
с разными именами методов в зависимости от типа возвращаемого значения или, как указал Джейк, с указанием типа объекта. - person Luchian Grigore; 19.09.2011

Предоставление таких примеров в псевдокоде немного сбивает с толку, шаблон сильно зависит от языка. Ваш пример выглядит как в C ++, но недопустим в C ++, потому что make возвращает Product по значению. Это полностью противоречит основной цели Factory - вернуть ссылку (указатель в случае C ++) на базовый класс. Некоторые ответы воспринимают это как C # или Java (я думаю), а другие - как C ++.

Factory шаблон основан на полиморфизме. Ключевым моментом является возврат ссылки на базовый класс Product. Потомки Factory будут создавать экземпляры конкретных классов.

person Andriy Tylychko    schedule 19.09.2011

У меня такая же путаница: «позвольте подклассам решать, какой класс создавать» - потому что в методе фабрики реализации, использующем new для создания объекта »- я ссылаюсь на первую книгу шаблонов проектирования, в которой об этом четко сказано:« Как в официальное определение, которое вы часто будете слышать, разработчики говорят, что позволяют подклассу решать, какой класс создать. Они говорят «решает» не потому, что шаблон позволяет подклассу определять время выполнения, а потому, что класс-создатель написан без знания фактического продукта, который будет создан, что определяется исключительно выбором подкласса, который используется "

person sujeet    schedule 23.09.2015

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

// contains some core business logic that relies on product // objects returned by the factory method. Subclasses can // indirectly change that business logic by overriding the // factory method and returning a different type of product // from it.

method render() is

// Call the factory method to create a product object. Button okButton = createButton() // Now use the product.

okButton.onClick(closeDialog) okButton.render()

// Concrete creators override the factory method to change the // resulting product's type.

class WindowsDialog extends Dialog is

method createButton():Button is return new WindowsButton()

class WebDialog extends Dialog is method createButton():Button is return new HTMLButton()

// The product interface declares the operations that all // concrete products must implement.

interface Button is

method render() method onClick(f)

// Concrete products provide various implementations of the // product interface.

class WindowsButton implements Button is method render(a, b) is // Render a button in Windows style.

is

method onClick(f) is // Bind a native OS click event.

class HTMLButton implements Button is

is

method render(a, b) is // Return an HTML representation of a button. method onClick(f) is

// Bind a web browser click event.

class Application is field dialog: Dialog

// The application picks a creator's type depending on the // current configuration or environment settings.

method initialize() is config = readApplicationConfigFile()

if (config.OS == "Windows") then dialog = new WindowsDialog() else if (config.OS == "Web") then

dialog = new WebDialog() else

throw new Exception("Error! Unknown operating system.")

// The client code works with an instance of a concrete // creator, albeit through its base interface. As long as // the client keeps working with the creator via the base // interface, you can pass it any creator's subclass.

method main() is

this.initialize() dialog.render()
person hossam rakha    schedule 07.11.2020