工厂模式。什么时候使用工厂方法?


Answers:


387

我喜欢以我的班级为“人”来思考设计模式,而模式是人们彼此交谈的方式。

因此,对我而言,工厂模式就像一家中介公司。您有需要不同数量工人的人。这个人可能知道他们所雇用的人需要的一些信息,仅此而已。

因此,当他们需要新员工时,他们会致电招聘机构并告诉他们他们需要什么。现在,要实际雇用某人,您需要了解很多东西-福利,资格验证等。但是,雇用人并不需要知道其中的任何内容-雇用中介可以处理所有这些。

同样,使用Factory可使使用者创建新对象,而不必知道其创建方式或它们的依赖关系的详细信息-他们只需要提供实际所需的信息即可。

public interface IThingFactory
{
    Thing GetThing(string theString);
}

public class ThingFactory : IThingFactory
{
    public Thing GetThing(string theString)
    {
        return new Thing(theString, firstDependency, secondDependency);
    }
}

因此,现在ThingFactory的使用者可以获取Thing,而无需了解Thing的依赖关系,除了来自使用者的字符串数据。


17
GetThing()的具体实现在哪里检索firstDependency和secondDependency的值?
Mikeyg36

88
有人可以告诉我这如何回答OP的问题吗?这仅描述了“工厂模式”是什么,然后添加了“工厂方法”的示例,该示例只是三个“工厂模式”之一。换句话说,我看不到任何地方的比较。
Forethinker 2013年

4
OP的问题明确提及within an object instead of a Factory class。我认为他的意思是您将ctor设为私有并使用静态方法实例化类(创建对象)的情况。但是要遵循此示例,必须首先实例化ThingFactory该类以获取Thing对象,这使该方法Factory class生效。
atiyar 2014年

4
抱歉,但是解释很糟糕,因为构造函数也可以隐藏依赖项的方式编写。您想要将依赖关系创建信息与依赖关系管理分开的关键背景信息丢失了。此外,问题是关于同一班级的,答案与之无关。
Christian Hujer,2015年

8
OP问何时。京急回答。尽管答案的风格值得称赞,但是在这个问题的语境中,这只是噪音。
8bitjunkie 2015年

96

应该将工厂方法视为构造函数的替代方法-多数情况是在构造函数表达能力不足的情况下(例如)。

class Foo{
  public Foo(bool withBar);
}

表现力不如:

class Foo{
  public static Foo withBar();
  public static Foo withoutBar();
}

当您需要复杂的过程来构造对象时,当构造需要您不希望为实际类创建的依赖项,需要构造其他对象等时,工厂类非常有用。


2
工厂班在哪里?
Koray Tugay 2014年

20
@KorayTugay:没有工厂类,只有工厂方法。问题在于何时使用工厂方法而不是工厂类。但是工厂方法比构造函数类更能替代构造函数。(尽管只谈论工厂课程,但我不知道为什么最高答案的评价如此之高)。
Rasmus Faber 2014年

5
应当注意,静态工厂方法与“四人帮:工厂方法”设计模式完全不同。
jaco0646 '16

76

我个人认为有意义的单独Factory类的一种情况是,当您尝试创建的最终对象依赖于其他几个对象时。例如,在PHP中:假设您有一个House对象,该对象又有一个Kitchen和一个LivingRoom对象,并且该对象内部也LivingRoom有一个TV对象。

实现此目的的最简单方法是让每个对象在其construct方法上创建其子级,但是如果属性是相对嵌套的,则在House创建失败时,您可能会花费一些时间来尝试准确地找出失败的原因。

另一种方法是执行以下操作(依赖注入,如果您喜欢幻想的术语):

$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);

在这里,如果创建House失败的过程只有一个地方可以看,但是每当需要一个新的块时就必须使用此块House很不方便。输入工厂:

class HouseFactory {
    public function create() {
        $TVObj = new TV($param1, $param2, $param3);
        $LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
        $KitchenroomObj = new Kitchen($param1, $param2);
        $HouseObj = new House($LivingroomObj, $KitchenroomObj);

        return $HouseObj;
    }
}

$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();

多亏了这里的工厂,创建a的过程House被抽象化了(因为您只想创建a时就不需要创建和设置每个依赖House),并且同时进行集中管理,这使得维护更加容易。还有其他原因使使用单独的factories有益(例如,可测试性),但是我发现这个特定的用例可以最好地说明Factory类如何有用。


1
有人会对此进行单元测试吗?我认为在类中使用“ new”关键字被认为是不好的做法,因为它不能进行单元测试。还是工厂是该规则的例外?
AgmLauncher

1
@AgmLauncher当我开始进行单元测试时,我也有同样的问题,请查看:stackoverflow.com/questions/10128780/…–
Mahn

1
没有得到这个。用于创建不同对象的参数如何准确地传递给HouseFactory类?
atiyar 2014年

1
@Mahn,您最终不会带来很多参数吗?
Pacerier 2014年

1
@Pacerier可以根据您的需要决定如何建模,但是您不必总是每个参数都传递给create方法。例如,如果您House将始终具有相同的类型,LivingRoom则将其参数硬编码在工厂类中而不是作为参数传递可能是有意义的。或者,如果您有几种类型的s,并且内部有针对每种类型进行硬编码的参数开关,则可能需要为方法提供type参数。HouseFactory::createLivingRoom
Mahn 2014年

19

清楚地区分使用工厂或工厂方法的想法很重要。两者都旨在解决互斥的不同类型的对象创建问题。

让我们具体谈谈“工厂方法”:

首先是,当您开发库或API时,这些库或API将用于进一步的应用程序开发,那么工厂方法是创建模式的最佳选择之一。原因背后;我们知道何时创建具有所需功能的对象,但是对象类型将保持不确定,或者将确定是否传递动态参数

现在的要点是,通过使用工厂模式本身可以实现大约相同的效果,但是如果将工厂模式用于上述突出显示的问题,则会给系统带来一个巨大的缺陷,那就是创建不同对象(子类对象)的逻辑将特定于某种业务状况,因此在将来需要将库的功能扩展到其他平台时(从技术上讲,您需要添加更多基本接口或抽象类的子类,因此除了现有对象外,工厂还将返回这些对象基于一些动态参数),那么每次您需要更改(扩展)工厂类的逻辑时,这将导致操作成本高昂,并且从设计角度来看并不理想。另一方面,如果是“工厂方法”

interface Deliverable 
{
    /*********/
}

abstract class DefaultProducer 
{

    public void taskToBeDone() 
    {   
        Deliverable deliverable = factoryMethodPattern();
    }
    protected abstract Deliverable factoryMethodPattern();
}

class SpecificDeliverable implements Deliverable 
{
 /***SPECIFIC TASK CAN BE WRITTEN HERE***/
}

class SpecificProducer extends DefaultProducer 
{
    protected Deliverable factoryMethodPattern() 
    {
        return new SpecificDeliverable();
    }
}

public class MasterApplicationProgram 
{
    public static void main(String arg[]) 
    {
        DefaultProducer defaultProducer = new SpecificProducer();
        defaultProducer.taskToBeDone();
    }
}

15

当您需要多个具有相同参数类型但行为不同的“构造函数”时,它们也很有用。


15

在以下情况下,最好在对象内部使用工厂方法

  1. 对象的类不知道必须创建什么确切的子类
  2. 设计对象的类,以便它创建的对象由子类指定
  3. 对象的类将其职责委托给辅助子类,并且不知道哪个确切的类将承担这些职责

在以下情况下,最好使用抽象工厂类:

  1. 您的对象不应该依赖于内部对象的创建和设计方式
  2. 链接对象组应一起使用,您需要满足此约束条件
  3. 对象应该由可能是父对象一部分的一系列链接对象中的一个进行配置
  4. 需要共享仅显示接口而不显示实现的子对象

9

来自的UML

在此处输入图片说明

产品:它定义了Factory方法创建的对象的接口。

ConcreteProduct:实现产品界面

创建者:声明工厂方法

ConcreateCreator: 实现Factory方法以返回ConcreteProduct的实例

问题陈述:使用定义游戏界面的工厂方法创建游戏工厂。

程式码片段:

import java.util.HashMap;


/* Product interface as per UML diagram */
interface Game{
    /* createGame is a complex method, which executes a sequence of game steps */
    public void createGame();
}

/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
    public Chess(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Chess game");
        System.out.println("Opponents:2");
        System.out.println("Define 64 blocks");
        System.out.println("Place 16 pieces for White opponent");
        System.out.println("Place 16 pieces for Black opponent");
        System.out.println("Start Chess game");
        System.out.println("---------------------------------------");
    }
}
class Checkers implements Game{
    public Checkers(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Checkers game");
        System.out.println("Opponents:2 or 3 or 4 or 6");
        System.out.println("For each opponent, place 10 coins");
        System.out.println("Start Checkers game");
        System.out.println("---------------------------------------");
    }
}
class Ludo implements Game{
    public Ludo(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Ludo game");
        System.out.println("Opponents:2 or 3 or 4");
        System.out.println("For each opponent, place 4 coins");
        System.out.println("Create two dices with numbers from 1-6");
        System.out.println("Start Ludo game");
        System.out.println("---------------------------------------");
    }
}

/* Creator interface as per UML diagram */
interface IGameFactory {
    public Game getGame(String gameName);
}

/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {

     HashMap<String,Game> games = new HashMap<String,Game>();
    /*  
        Since Game Creation is complex process, we don't want to create game using new operator every time.
        Instead we create Game only once and store it in Factory. When client request a specific game, 
        Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
    */

    public GameFactory(){

        games.put(Chess.class.getName(),new Chess());
        games.put(Checkers.class.getName(),new Checkers());
        games.put(Ludo.class.getName(),new Ludo());        
    }
    public Game getGame(String gameName){
        return games.get(gameName);
    }
}

public class NonStaticFactoryDemo{
    public static void main(String args[]){
        if ( args.length < 1){
            System.out.println("Usage: java FactoryDemo gameName");
            return;
        }

        GameFactory factory = new GameFactory();
        Game game = factory.getGame(args[0]);
        if ( game != null ){                    
            game.createGame();
            System.out.println("Game="+game.getClass().getName());
        }else{
            System.out.println(args[0]+  " Game does not exists in factory");
        }           
    }
}

输出:

java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess

本示例Factory通过实现展示了一个类FactoryMethod

  1. Game是所有类型游戏的界面。它定义了复杂的方法:createGame()

  2. Chess, Ludo, Checkers 是不同的游戏变体,可为 createGame()

  3. public Game getGame(String gameName)FactoryMethodIGameFactory

  4. GameFactory在构造函数中预先创建不同类型的游戏。它实现IGameFactory工厂方法。

  5. 游戏名称作为命令行参数传递给 NotStaticFactoryDemo

  6. getGame中的GameFactory接受游戏名称并返回相应的Game对象。

厂:

创建对象而不将实例化逻辑暴露给客户端。

工厂方法

定义用于创建对象的接口,但让子类决定实例化哪个类。Factory方法允许类将实例化延迟到子类

用例:

什么时候使用:Client不知道在运行时需要创建什么具体的类,而只是想获得一个可以完成工作的类。


谢谢您的主题演讲部分,这对我来说很简洁。但是,“ getArea()是Shape接口中的工厂方法”是无法想象的,因为方法getArea永远不会实例化任何类,它只是进行计算。创建对象的接口,但请让子类决定实例化哪个类。
reco

getArea()不是工厂方法在所有
克拉尼奥

1
我有不同的意见-专家请验证并做笔记。1.客户端(或调用者)需要感兴趣的对象...因此,不需要调用新的GameFactory(),而Factory类应该具有静态的getInstance()。2.如果是,则也可以使用 games.put(Chess.class.getName( ),new Chess()); 将始终返回国际象棋的相同引用[如果实现为静态]-如何处理该方案最有效的方法?
Arnab Dutta

我举了非静态工厂的例子。如果需要,可以使用静态块和方法来实现它。关于您的查询:1。客户将致电Factory获得游戏。2.我把对象一次,所有得到的将返回同一个实例-国际象棋的相同referenc.e返回每一个GET
拉温德拉巴布

6

这真的是一个品味问题。可以根据需要对工厂类进行抽象/接口处理,而工厂方法的重量较轻(并且由于没有定义的类型,因此也易于测试),但它们需要类似于服务的众所周知的注册点定位器,但用于定位工厂方法)。


4

当工厂类返回的对象类型具有私有构造函数,不同的工厂类在返回的对象上设置不同的属性或特定的工厂类型与其返回的具体类型结合在一起时,工厂类非常有用。

WCF使用ServiceHostFactory类在不同情况下检索ServiceHost对象。IIS使用标准的ServiceHostFactory检索.svc文件的ServiceHost实例,但是WebScriptServiceHostFactory用于将序列化返回给JavaScript客户端的服务。ADO.NET数据服务具有其自己的特殊DataServiceHostFactory,而ASP.NET具有其ApplicationServicesHostFactory,因为其服务具有私有构造函数。

如果只有一个消耗工厂的类,则可以在该类中使用工厂方法。


2

当您必须设计Order和Customer类时,请考虑一种情况。为了简单起见和最初的要求,您不需要Order类的工厂,而无需在应用程序中填充许多“ new Order()”语句。事情进展顺利。

现在出现了一个新的要求,即没有客户关联(新的依赖关系)就不能实例化Order对象。现在,您有以下注意事项。

1-您创建了构造函数重载,该重载仅适用于新的实现。(不能接受的)。2-您更改Order()签名并更改每个发票。(这不是一个好的做法,是真正的痛苦)。

相反,如果您为订单类创建了工厂,则只需更改一行代码即可。我建议为几乎每个聚合关联使用Factory类。希望能有所帮助。


1

如果要在使用方面创建其他对象。它是有益的。

public class factoryMethodPattern {
      static String planName = "COMMERCIALPLAN";
      static int units = 3;
      public static void main(String args[]) {
          GetPlanFactory planFactory = new GetPlanFactory();
          Plan p = planFactory.getPlan(planName);
          System.out.print("Bill amount for " + planName + " of  " + units
                        + " units is: ");
          p.getRate();
          p.calculateBill(units);
      }
}

abstract class Plan {
      protected double rate;

      abstract void getRate();

      public void calculateBill(int units) {
            System.out.println(units * rate);
      }
}

class DomesticPlan extends Plan {
      // @override
      public void getRate() {
            rate = 3.50;
      }
}

class CommercialPlan extends Plan {
      // @override
      public void getRate() {
            rate = 7.50;
      }
}

class InstitutionalPlan extends Plan {
      // @override
      public void getRate() {
            rate = 5.50;
      }
}

class GetPlanFactory {

      // use getPlan method to get object of type Plan
      public Plan getPlan(String planType) {
            if (planType == null) {
                  return null;
            }
            if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
                  return new DomesticPlan();
            } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
                  return new CommercialPlan();
            } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
                  return new InstitutionalPlan();
            }
            return null;
      }
}


1

我认为这将取决于要带入代码中的松散耦合度。

工厂方法很好地解耦了事物,但是工厂类没有。

换句话说,使用工厂方法比使用简单工厂(称为工厂类)更容易进行更改。

请看以下示例:https : //connected2know.com/programming/java-factory-pattern/。现在,假设您想带来一个新动物。在Factory类中,您需要更改Factory,但是在factory方法中,不需要,您只需要添加一个新的子类。


0

工厂类更重量级,但可以为您带来某些优势。在需要从多个原始数据源构建对象的情况下,它们仅允许您将构建逻辑(以及数据的聚合)封装在一个地方。在那里可以抽象地测试它,而不必考虑对象接口。

我发现这是一个有用的模式,尤其是在我无法替换ORM并使其不足并且想要有效地实例化DB表联接或存储过程中的许多对象的情况下。


0

我把工厂比作图书馆的概念。例如,您可以拥有一个用于处理数字的库,另一个用于处理形状的库。您可以将这些库的功能存储在逻辑上命名为Numbers或的目录中Shapes。这些是通用类型,对于形状,可以包括整数,浮点数,dobules,longs或矩形,圆形,三角形,五边形。

工厂petter使用多态性,依赖项注入和控制反转。

工厂模式的既定目的是: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

假设您正在构建操作系统或框架,并且正在构建所有离散组件。

这是PHP中工厂模式概念的简单示例。我可能不会百分百地接受所有这些,但是它只是作为一个简单的例子。我不是专家。

class NumbersFactory {
    public static function makeNumber( $type, $number ) {
        $numObject = null;
        $number = null;

        switch( $type ) {
            case 'float':
                $numObject = new Float( $number );
                break;
            case 'integer':
                $numObject = new Integer( $number );
                break;
            case 'short':
                $numObject = new Short( $number );
                break;
            case 'double':
                $numObject = new Double( $number );
                break;
            case 'long':
                $numObject = new Long( $number );
                break;
            default:
                $numObject = new Integer( $number );
                break;
        }

        return $numObject;
    }
}

/* Numbers interface */
abstract class Number {
    protected $number;

    public function __construct( $number ) {
        $this->number = $number;
    }

    abstract public function add();
    abstract public function subtract();
    abstract public function multiply();
    abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Integer Implementation */
class Integer extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Short Implementation */
class Short extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Double Implementation */
class Double extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Long Implementation */
class Long extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}

$number = NumbersFactory::makeNumber( 'float', 12.5 );

我了解这里发生的事情,但我不明白这是什么意思。NumbersFactory::makeNumber( 'float', 12.5 );只是说new Float(12.5);我知道我需要一个什么给了我什么Float?这就是我对工厂不了解的东西。
BadHorsie 2015年

它使您可以选择不同的实现,而不仅限于一个。建立了一个接口,所有实现都必须保证并尊重它。
罗伯特·罗查
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.