工厂方法设计模式

7
根据这本书:
工厂模式的本质是“定义一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法允许一个类将实例化推迟到子类中。”
假设我有一个Creator类:
class Product; //this is what the Factory Method should return
class Creator {
    public:
        Creator()   //ctor
        { //... }

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

好的,这是我的Creator类,但我不理解

工厂方法让一个类将实例化推迟到了子类

它与子类有什么关系? 我应该使用子类做什么?

能否给我一些例子?

8个回答

11

你的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 实例,其中 BookComputer 是有效的子类。

还有其他的工厂惯用语,例如具有工厂的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;
为了澄清事情,由于似乎在抽象工厂工厂方法设计模式之间存在一些混淆,让我们来看一个具体的例子:

使用抽象工厂

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鉴别器时,我们正在使用参数化工厂方法——一种知道如何创建不同类型对象的方法。但这可能会出现在抽象工厂工厂方法模式中。一个简短的技巧:如果你扩展了工厂类,那么你正在使用抽象工厂。如果你扩展了带有创建方法的类,则表示你正在使用工厂方法。


所以,在你的代码中,ProductFactory 是一个抽象工厂,而 make 是工厂方法,对吗? - Alcott
“Abstract Factory”和“Factory Method”的区别本身就是一个问题 :) 简而言之,当您像我在示例中所做的那样使用类型鉴别器时,我们正在使用“参数化工厂方法” - 一种知道如何创建不同类型对象的方法。但是,这可以出现在抽象工厂或工厂方法模式中。一个简短的技巧:如果您扩展工厂类,则使用“抽象工厂”。如果您使用_factory methods_扩展类,则使用“工厂方法”。如果仍然不清楚区别,请在SO上提出不同的问题。 - Jorge Ferreira
为了明确,我的答案不使用“工厂方法”设计模式,而是使用“抽象工厂”设计模式。 - Jorge Ferreira

3
工厂模式仅意味着存在某个工厂类或方法,其责任是为您创建对象;而不是您自己实例化它们。就像汽车在工厂里生产一样,这样您就不必自己制造了。
它与子类无关,然而作者可能想说的是,工厂通常可以根据您的参数返回基类的派生实现,因为该子类可能会执行您在参数中请求的操作。
例如,WebRequest.Create("http://www.example.com") 将返回 HttpWebRequest,但 WebRequest.Create("ftp://www.example.com") 将返回 FtpWebRequest,因为两者具有不同的协议,由不同的类实现,但公共接口相同,因此 API 的使用者不必做出这个决定。

1

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; }

工厂模式隐藏了构建不同产品类型所需的条件细节。其次,在我看来,从API用户的角度来看,通常更容易看到有哪些产品类型(通常是从枚举中)并且更容易从单个创建点创建它们。


这是否意味着,工厂方法可能会产生(返回)多种产品,但在工厂方法内部创建和返回哪种产品是根据某些条件(例如传递给工厂方法的参数)决定的。 - Alcott
@Alcott,请查看我回答中添加的更多细节。 - Jake

1

简单明了:

工厂中,检查所请求的“子类”以实例化,因此“让子类决定要实例化哪个类”
您在工厂类中使用条件语句进行决策。

“为创建对象定义一个接口或抽象类”。显然,您将对象存储到接口的引用中,客户端不知道返回的是哪个具体类的对象。(因此,您定义了一个接口来创建对象。)


0

我只能假设他的意思是这样的:

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

然而,这不是创建工厂的标准方式。


根据返回类型具有不同的方法名称,或者正如Jake指出的那样,指定对象类型。 - Luchian Grigore

0
我有同样的困惑,“让子类决定实例化哪个类” - 因为在实现工厂方法时使用 new 来创建对象” - 我参考了《Head First 设计模式》一书,其中明确说明如下 - “正如官方定义中所说,你经常会听到开发人员说让子类决定实例化哪个类。他们说“决定”,不是因为该模式允许子类自己在运行时决定,而是因为创建者类是在不知道将要创建的实际产品的情况下编写的,这完全取决于所使用的子类的选择。”

0

提供伪代码示例有点令人困惑,因为模式非常依赖于语言。你的示例看起来像是C++,但在C++中是无效的,因为make通过值返回Product。这完全违反了Factory的主要目标 - 返回基类的引用(在C++中是指针)。有些答案将其视为C#或Java(我猜),而其他人则视为C++。

Factory模式依赖于多态性。关键点是返回对基类Product的引用。 Factory的子类将创建具体类的实例。


0

工厂方法是一种创建型设计模式,它提供了一个在超类中创建对象的接口,但允许子类更改将被创建的对象类型。本示例说明了如何使用工厂方法创建跨平台UI元素,而不将客户端代码耦合到具体的UI类。

// 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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接