什么是静态工厂方法?


261

什么是“静态工厂”方法?


另一种静态工厂方法是使用依赖项注入。
CMCDragonkai 2014年

1
@ThangPham Jason Owen的答案是有缺陷的,因为它声称是在谈论工厂方法模式,这与它实际讨论的静态工厂方法模式有很大不同。因此,尽管它很好地回答了实际问题,但我认为它在当前状态下是不可接受的,因为它带来了不相关的模式,并加剧了人们对两种模式之间的差异已经难以置信的普遍困惑。
Theodore Murdock 2014年

@CMCDragonkai我认为依赖注入和静态工厂是不同的。即使在依赖注入的情况下也可能需要静态工厂,以实例化要注入的依赖。
卡兰·汉娜

Answers:


126

我们避免直接访问数据库连接,因为它们消耗大量资源。因此getDbConnection,如果我们低于限制,我们将使用静态工厂方法来创建连接。否则,它将尝试提供“备用”连接,如果不存在则失败,并显示异常。

public class DbConnection{
   private static final int MAX_CONNS = 100;
   private static int totalConnections = 0;

   private static Set<DbConnection> availableConnections = new HashSet<DbConnection>();

   private DbConnection(){
     // ...
     totalConnections++;
   }

   public static DbConnection getDbConnection(){

     if(totalConnections < MAX_CONNS){
       return new DbConnection();

     }else if(availableConnections.size() > 0){
         DbConnection dbc = availableConnections.iterator().next();
         availableConnections.remove(dbc);
         return dbc;

     }else {
         throw new NoDbConnections();
     }
   }

   public static void returnDbConnection(DbConnection dbc){
     availableConnections.add(dbc);
     //...
   }
}

如果我理解正确,是否可以将availableConnections.add(db)添加到方法returnDbConnection(DbConnection db)中?
张海峰

@haifzhan,这并不是真的要完整,但可以。
马修·弗拉申

@MatthewFlaschen还应该在返回连接时减少totalConnections吗?
滚石

@Sridhar,不,这是存在的连接数(已跟踪(因此不会创建超过MAX_CONNS的连接数)),而不是循环的数量。
马修·弗拉申

如果DbConnection是Closeable,@ sonFlaschen您的方法将被sonarcube作为阻止程序错误捕获。
阿万·比鲁

477

静态工厂方法模式是一种封装对象的创建。如果没有工厂方法,则只需直接调用类的构造函数Foo x = new Foo()。使用这种模式,您可以改为调用factory方法: Foo x = Foo.create()。构造函数被标记为私有,因此只能从类内部调用它们,并且将工厂方法标记为,static以便无需首先具有对象就可以对其进行调用。

这种模式有一些优点。一种是工厂可以从许多子类(或接口的实现者)中进行选择并返回。这样,调用者可以通过参数指定所需的行为,而不必了解或理解潜在的复杂类层次结构。

正如Matthew和James指出的那样,另一个优势是控制对有限资源(例如连接)的访问。这是一种实现可重用对象池的方法-而不是构建,使用和拆除对象,如果构造和销毁是昂贵的过程,则一次构造并回收它们可能更有意义。工厂方法可以返回一个现有的,未使用的实例化对象(如果有),或者如果对象计数低于某个下限阈值则构造一个对象,或者如果该对象null高于上限阈值则抛出异常或返回。

根据Wikipedia上的文章,多种工厂方法还允许对类似参数类型进行不同的解释。通常,构造函数与类具有相同的名称,这意味着您只能有一个具有给定签名的构造函数。工厂并不是那么受约束,这意味着您可以有两种接受相同参数类型的不同方法:

Coordinate c = Coordinate.createFromCartesian(double x, double y)

Coordinate c = Coordinate.createFromPolar(double distance, double angle)

正如Rasmus所指出的,这也可以用来提高可读性。


32
请注意,静态工厂方法与“设计模式” [Gamma95,p。1]中的工厂方法模式不同。107]。此项中描述的静态工厂方法在“设计模式”中没有直接等效的方法。
Josh Sunshine 2013年

对我来说,这个答案的回报是对可合并对象的引用。这正是我使用工厂方法模式的方式。提供了工厂方法来帮助控制对象的生命周期:“创建”从池中拉出对象或在池为空时创建新实例,“销毁”将其返回到池中以供将来重用。
MutantXenu 2013年

2
无论您在本文中所说的“工厂方法模式”是什么地方,都应该使用错误的术语。另外,您所链接的Wikipedia文章的模式与您所讨论的模式不同。工厂方法模式可能应该被称为“工厂接口”模式,因为它涉及使用几个实现工厂接口的工厂对象,以通过接受工厂对象来使单个算法能够产生并使用接口实例可以产生特定的所需子类。
Theodore Murdock 2014年

8
我同意@TheodoreMurdock。您使用了错误的术语。您正在谈论的是Joshua Bloch的“静态工厂方法”,但是您使用GoF的“工厂方法模式”一词。请对其进行编辑,以免其他人误解。
emeraldhieu 2014年

1
注意,构造函数不必是私有的。一个类可以提供公共静态工厂方法和构造函数。
凯文

171

注意!“该静态工厂方法一样的工厂方法模式”(三)有效的Java,约书亚·布洛克。

工厂方法:“定义一个用于创建对象的接口,但是让实现该接口的类决定要实例化哪个类。工厂方法使一个类将实例化推迟到子类。”(c)GoF。

“静态工厂方法只是返回类实例的静态方法。” (c)有效的Java,约书亚·布洛赫(Joshua Bloch)。通常,此方法在特定的类中。

区别:

静态工厂方法的关键思想是获得对对象创建的控制,并将其从构造函数委托给静态方法。要创建对象的决定就像在方法之外在“抽象工厂”中做出的决定一样(通常,但并非总是如此)。而Factory Method的关键(!)想法是委托在Factory Method中创建要创建的类的实例的决定。例如,经典的Singleton实现是静态工厂方法的一种特殊情况。常用的静态工厂方法示例:

  • 的价值
  • getInstance
  • newInstance

您能告诉我new A()和A.newInstance()之间的区别吗?在A.newInstance内部我们应该怎么做?

69

可以通过静态工厂方法提高可读性:

比较

public class Foo{
  public Foo(boolean withBar){
    //...
  }
}

//...

// What exactly does this mean?
Foo foo = new Foo(true);
// You have to lookup the documentation to be sure.
// Even if you remember that the boolean has something to do with a Bar
// you might not remember whether it specified withBar or withoutBar.

public class Foo{
  public static Foo createWithBar(){
    //...
  }

  public static Foo createWithoutBar(){
    //...
  }
}

// ...

// This is much easier to read!
Foo foo = Foo.createWithBar();

所以我尝试实现您的示例,但不确定如何运行。Ar这两个方法createWithBar和createWithoutBar应该调用Foo类内的2个私有构造函数吗?
Essej '16

@Baxtex:是的。每个都将调用一个私有构造函数。也许就一样: private Foo(boolean withBar){/*..*/} public static Foo createWithBar(){return new Foo(true);} public static Foo createWithoutBar(){return new Foo(false);}
Rasmus Faber

我认为这不是很好的“规模”。如果您有三个或更多参数,那么如何使用该想法并为该方法创建一个漂亮的名称?
Dherik '16

@Dherik:那么您可能应该改用构建器模式:new FooBuilder()。withBar()。withoutFoo()。withBaz()。build();
Rasmus Faber

21
  • 与构造函数不同,它们具有可以澄清代码的名称。
  • 无需在每次调用时都创建一个新对象-可以根据需要缓存和重用对象。
  • 可以返回其返回类型的子类型-特别是可以返回调用者未知其实现类的对象。这是在许多使用接口作为静态工厂方法的返回类型的框架中非常有价值且广泛使用的功能。

来自http://www.javapractices.com/topic/TopicAction.do?Id=21


18

归结为维护性。最好的解决方法是,每当您使用new关键字创建对象时,便会将正在编写的代码耦合到实现上。

工厂模式使您可以将创建对象的方式与处理对象的方式分开。当使用构造函数创建所有对象时,实际上是在将使用该对象的代码硬连线到该实现。使用您的对象的代码“依赖于”该对象。从表面上看,这似乎没什么大不了,但是当对象改变时(想改变构造函数的签名,或子类化对象),则必须返回并重新布线。

如今,工厂很大程度上被抛在一边,而赞成使用依赖注入,因为它们需要大量样板代码,而这些代码实际上很难维护。依赖注入在本质上与工厂等效,但是可以让您指定如何以声明方式(通过配置或注释)将对象连接在一起。


3
您是说工厂需要纾困吗?;)
约翰·法瑞尔

4
哈哈!在这个时代,我认为我们所有人都可以使用救助... :)
09年

11

如果类的构造函数是私有的,则您无法从其外部为类创建对象。

class Test{
 int x, y;
 private Test(){
  .......
  .......
  }
}

我们不能从外部为上述类创建对象。因此,您不能从类外部访问x,y。那么该类的用途是什么?
这是答案:FACTORY方法。
在上面的类中添加以下方法

public static Test getObject(){
  return new Test();
}

因此,现在您可以从其外部为该类创建一个对象。就像这样

Test t = Test.getObject();

因此,通过执行其私有构造函数返回类的对象的静态方法称为FACTORY方法


1
您为什么称其为“解决方案”?声明私有构造函数不是“问题”,它是一种设计模式。
Ojonugwa Jude Ochalifu

1
更新。无论如何
Santhosh

嗨@Santhosh,我担心的是,为什么不让私有构造函数公开?如果您不想创建子类,那对我来说很好,但是如果我不想成为私有构造函数,那么相Static Factory Method对于公共构造函数,我们有什么好处吗?

9

我以为我会对我所知道的内容加一些启发。我们在电脑中广泛使用了这种技术recent android project。代替creating objects using new operator您也可以使用static method实例化一个类。代码清单:

//instantiating a class using constructor
Vinoth vin = new Vinoth(); 

//instantiating the class using static method
Class Vinoth{
  private Vinoth(){
  }
  // factory method to instantiate the class
  public static Vinoth getInstance(){
    if(someCondition)
        return new Vinoth();
  }
}

静态方法支持条件对象的创建:每次调用构造函数时,都会创建一个对象,但您可能不希望这样做。假设您只想检查某些条件,那么您想创建一个新对象。除非满足条件,否则您不会每次都创建一个新的Vinoth实例。

另一个示例取自Effective Java

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
}

此方法将布尔基元值转换为布尔对象引用。该Boolean.valueOf(boolean)方法向我们说明了,它从不创建对象。static factory methods从重复返回相同对象的能力invocations允许类对任何时刻存在的实例保持严格的控制。

Static factory methods区别在于,constructors他们可以返回object任意subtype返回类型的。这种灵活性的一种应用是API可以返回对象而无需公开其类。以这种方式隐藏实现类会产生非常紧凑的API。

Calendar.getInstance()是一个很好的例子为上述的,它创建取决于区域设置的BuddhistCalendarJapaneseImperialCalendar或通过默认的Georgian

我可以想到的另一个示例是Singleton pattern,您可以使构造函数私有化,然后创建一个自己的getInstance方法,以确保始终只有一个实例可用。

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

4

工厂方法一种抽象对象实例化的方法。通常,当您知道需要一个实现某个接口的类的新实例但不知道实现类时,工厂就很有用。

当使用相关类的层次结构时,这很有用,GUI工具包就是一个很好的例子。您可以简单地硬编码对构造函数的调用,以实现每个小部件的具体实现,但是如果您想将一个工具箱换成另一个,则有很多地方可以更改。通过使用工厂,您减少了需要更改的代码量。


假设您的工厂返回的是接口类型,而不是要处理的具体类。
比尔·林奇

1
这个答案是关于工厂方法设计模式而不是静态工厂方法的。静态工厂方法只是返回类实例的公共静态方法。有关更多详细信息,请参见《有效Java》的第2章。
Josh Sunshine

4

静态工厂的优点之一是API可以返回对象而无需将其类公开。这导致非常紧凑的API。在Java中,这是通过Collections类实现的,该类隐藏了约32个类,这使其Collection API非常紧凑。


2

当您要确保只有一个实例将返回要使用的具体类时,静态工厂方法会很好。

例如,在一个数据库连接类中,您可能只希望一个类来创建数据库连接,因此,如果您决定从Mysql切换到Oracle,则只需更改一个类中的逻辑,其余的应用程序将使用新的连接。

如果要实现数据库池,那么也可以在不影响应用程序其余部分的情况下完成。

它可以保护应用程序的其余部分免受您可能对工厂进行的更改(这是目的)。

之所以是静态的,是因为如果您要跟踪某些有限的资源(套接字连接或文件句柄的数量),则此类可以跟踪已经传递和返回的资源数量,因此您不会耗尽资源有限。


2

使用私有构造函数的静态工厂方法的优点之一(必须为外部类限制对象的创建,以确保不会在外部创建实例)是可以创建实例控制的类。并且实例控制的类保证程序运行期间不存在两个相等的不同实例(a.equals(b),并且仅当a == b时),这意味着您可以使用==运算符而不是equals方法检查对象的相等性。,根据有效Java。

静态工厂方法从重复调用返回相同对象的能力允许类对任何时刻存在的实例保持严格的控制。据说这样做的类是实例控制的。编写实例控制的类有多种原因。实例控件允许类保证它是单例(项目3)或不可实例化(项目4)。而且,它允许不可变的类(第15项)保证不存在两个相等的实例:a.equals(b)当且仅当a == b时。如果一个类保证了这一点,那么它的客户可以使用==运算符而不是equals(Object)方法,这可能会提高性能。枚举类型(项30)提供了此保证。

来自有效Java的Joshua Bloch(第1页,第1项)


1

静态的

用关键字“ static”声明的成员。

工厂方法

创建和返回新对象的方法。

在Java中

编程语言与“静态”的含义有关,而与“工厂”的定义无关。


@Victor响应不需要包含参数。事实应该足够:如果有争议,可以通过论点或引用来支持。我不知道这里有什么争议。
2014年

我说过,因为答案太短了,在第一眼,第二眼和第三眼的眼神中,对它的理解确实不够好,无论如何,要由OP提出。没关系,我已经改变主意并尝试删除下降表决,但我不能。
维克多

@Victor定义“太短”。有时,真正的答案只是“是”,“否”,“都不是”或“都是”。“太短”完全是主观的。我仍然不明白你的原始评论。您不需要参数即可支持定义。根据定义。我已经编辑了答案,因此现在可以删除下降投票。
2014年

当然是主观的...所有stackoverflow答案都是主观的,实际上它们与作者级别的知识和意愿有关。我真的不明白您的回答,也许是简短的。
维克多

@Victor Rubbish。事实不是主观的,也不是可以从事实或公理推论得出的问题。另一方面,“太短”是主观的,我已经解决了。这个答案是基本相同,这一次,你还没有评论。您的评论仍然晦涩难懂。
罗恩侯爵

1

Java实现包含实用程序类java.util.Arraysjava.util.Collections,它们都包含静态工厂方法,其示例以及使用方法:

另外,java.lang.String类也具有此类静态工厂方法

  • String.format(...), String.valueOf(..), String.copyValueOf(...)
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.