【问题标题】:factory method design pattern工厂方法设计模式
【发布时间】:2011-11-20 01:15:38
【问题描述】:

根据书:

工厂模式的本质是“为 创建一个对象,但让子类决定哪个类 实例化。 Factory 方法允许类将实例化延迟到 子类。

假设我有一个 Creator 类:

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

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

好的,这是我的 Creator 课程,但我不明白

Factory 方法允许类将实例化推迟到子类

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

谁能给我举个例子?

【问题讨论】:

    标签: design-patterns factory-method


    【解决方案1】:

    您的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;
    

    为了清楚起见,由于Abstract FactoryFactory 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 FactoryFactory Method 模式中。一个小技巧:如果你正在扩展工厂类,你正在使用抽象工厂。如果您使用创建方法扩展类,那么您使用的是工厂方法。

    【讨论】:

    • 那么,在您的代码中,ProductFactory 是一个抽象工厂,而 make 是工厂方法,对吧?
    • Abstract FactoryFactory Method 之间的区别本身就是一个问题 :) 简而言之,当您像我在示例中那样使用类型鉴别器时,我们正在使用“参数化工厂方法" - 一种知道如何创建不同类型对象的方法。但这可以出现在抽象工厂或工厂方法模式中。一个小技巧:如果你正在扩展你正在使用的工厂类Abstract Factory。如果您使用 工厂方法 扩展类,那么您使用的是Factory Methods。如果差异仍然不清楚,请在 SO 上提出不同的问题。
    • 说清楚。我的回答不是使用Factory method 设计模式而是Abstract Factory 设计模式。
    【解决方案2】:

    工厂模式仅仅意味着有一些工厂类或方法负责为你创建对象;而不是你自己实例化它们。就像汽车是在工厂制造的,所以您不必这样做。

    它与子类无关,但是作者可能试图说工厂通常可以根据您的参数返回基类的派生实现,因为该子类可能会执行您在参数中要求的操作。

    例如 WebRequest.Create("http://www.example.com") 会返回给我 HttpWebRequest 但 WebRequest.Create("ftp://www.example.com") 会返回给我 FtpWebRequest 因为两者都有不同的协议,由不同的类实现,但公共接口是同样,因此我的 API 的使用者不必做出此决定。

    【讨论】:

      【解决方案3】:

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

      Factory 隐藏了构建各种产品类型所需的条件细节。其次,恕我直言,从 API 用户的角度来看,通常更容易查看有哪些产品类型(通常来自枚举),并且更容易从单个创建点创建它们。

      【讨论】:

      • 是不是一个工厂方法可能产生(返回)多个产品,但是在工厂方法内部创建和返回哪个是根据某些条件(例如传递给工厂方法的args)决定的.
      • @Alcott 请查看添加到我的答案中的更多详细信息。
      【解决方案4】:

      简单而简短:

      工厂中,检查请求实例化哪个“子类”,因此"let the subclasses decide which class to instantiate"
      您在必须做出决定的工厂类中使用条件语句。

      "define an interface or abstract class for creating an object"。显然,您将对象存储到接口的引用中,并且客户端不知道返回了哪个具体类的对象。 (所以你定义了一个接口来创建一个对象)。

      【讨论】:

        【解决方案5】:

        我只能假设他是这个意思:

        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 指出的那样,指定对象类型。
        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

            工厂方法是一种创建型设计模式,它提供了在超类中创建对象的接口,但允许子类更改将要创建的对象的类型。此示例说明如何使用工厂方法创建跨平台 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()
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-11
              • 1970-01-01
              • 2023-04-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多