关于java:什么是静态工厂方法?

What are static factory methods?

什么是"静态工厂"方法?


静态工厂方法模式是一种封装对象创建的方法。如果没有工厂方法,只需直接调用类的构造函数:Foo x = new Foo()。使用这种模式,您将改为调用工厂方法:Foo x = Foo.create()。构造函数被标记为private,因此只能从类内部调用它们,工厂方法被标记为static,这样就可以在没有对象的情况下调用它。

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

另一个优势是,正如马修和詹姆斯所指出的,控制对有限资源(如连接)的访问。这是一种实现可重用对象池的方法,而不是构建、使用和分解对象,如果构建和销毁过程很昂贵,那么一次构建并回收它们可能更有意义。工厂方法可以返回一个现有的、未使用的实例化对象(如果它有一个),或者如果对象计数低于某个较低的阈值,则构造一个;如果它高于较高的阈值,则抛出异常或返回null

根据维基百科上的文章,多个工厂方法也允许对类似的参数类型进行不同的解释。通常,构造函数与类具有相同的名称,这意味着您只能有一个具有给定签名的构造函数。工厂没有这样的限制,这意味着您可以使用两种不同的方法来接受相同的参数类型:

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

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

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


注意!"静态工厂方法不同于工厂方法模式"(c)有效Java,Joshua Bloch。

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

"静态工厂方法只是一个静态的方法,它返回一个类的实例。"(c)有效的Java,Joshua Bloch。通常这个方法在一个特定的类中。

差异:

静态工厂方法的核心思想是控制对象的创建,并将其从构造函数委托给静态方法。要创建的对象的决策类似于在抽象工厂中在方法之外做出的决策(在常见情况下,但并非总是如此)。而键(!)工厂方法的思想是委托决定在工厂方法内创建什么类实例。例如,经典的单例实现是静态工厂方法的一个特例。常用的静态工厂方法示例:

  • 价值
  • 获取实例
  • 新态度


我们避免直接访问数据库连接,因为它们是资源密集型的。所以我们使用一个静态工厂方法getDbConnection,如果我们低于限制,它会创建一个连接。否则,它会尝试提供一个"备用"连接,如果没有,则会出现异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);
     //...
   }
}


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

比较

1
2
3
4
5
6
7
8
9
10
11
12
13
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Foo{
  public static Foo createWithBar(){
    //...
  }

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

// ...

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


  • have names, unlike constructors, which can clarify code.
  • do not need to create a new object upon each invocation - objects
    can be cached and reused, if necessary.
  • can return a subtype of their return type - in particular, can
    return an object whose implementation class is unknown to the caller.
    This is a very valuable and widely used feature in many frameworks
    which use interfaces as the return type of static factory methods.

来自http://www.javapractices.com/topic/topication.do?ID=21


这一切归结为可维护性。最好的方法是,每当使用new关键字创建对象时,都要将编写的代码耦合到实现中。

工厂模式允许您将创建对象的方式与处理对象的方式分开。当使用构造函数创建所有对象时,实际上是硬连接使用对象的代码到该实现。使用对象的代码"依赖"该对象。表面上这似乎不是什么大问题,但当对象发生变化(考虑更改构造函数的签名,或子类化对象)时,您必须返回并在任何地方重新布线。

如今,很多工厂都被忽视了,转而支持使用依赖注入,因为它们需要大量的样板代码,而这些代码却有点难以维护。依赖注入基本上等同于工厂,但允许您指定如何以声明方式(通过配置或注释)将对象连接在一起。


如果类的构造函数是私有的,则不能从类的外部为类创建对象。

1
2
3
4
5
6
7
class Test{
 int x, y;
 private Test(){
  .......
  .......
  }
}

我们不能从上面的类外部为它创建对象。所以你不能从类外访问x,y。那么这个类的用途是什么呢?< BR>答案如下:工厂法。
在上面的类中添加下面的方法

1
2
3
public static Test getObject(){
  return new Test();
}

所以现在您可以从这个类的外部为它创建一个对象。喜欢的方式…

1
Test t = Test.getObject();

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


我想我会在这篇文章中增加一些我所知道的信息。我们在我们的recent android project中广泛使用了这种技术。除了creating objects using new operator之外,还可以使用static method来实例化类。代码列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
//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实例。

另一个例子来自有效的Java。

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

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

static factory methods是指,与constructors不同,它们可以返回其返回类型的任何subtypeobject。这种灵活性的一个应用是,API可以返回对象而不公开其类。以这种方式隐藏实现类会导致非常紧凑的API。

calendar.getInstance()是上面的一个很好的例子,它根据区域设置a BuddhistCalendarJapaneseImperialCalendar或默认情况下的Georgian创建。

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

1
2
3
4
5
6
7
8
9
10
11
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;
    }
}

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

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


静态工厂的一个优点是,API可以返回对象而不公开其类。这导致了非常紧凑的API。在Java中,这是通过集合类实现的,集合类隐藏在32个类中,这使得集合API非常紧凑。


Java实现包含实用工具类JavaUTL数组和JavaUTL集合,它们都包含静态工厂方法、它的示例以及如何使用:

  • Arrays.asList("1","2","3")

  • Collections.synchronizedList(..), Collections.emptyList(), Collections.unmodifiableList(...)(只有一些示例可以检查java docs中的mor方法示例https://docs.oracle.com/javase/8/docs/api/java/util/collections.html)

另外java.lang.string类也有这样的静态工厂方法:

  • String.format(...), String.valueOf(..), String.copyValueOf(...)


static

用关键字"static"声明的成员。

factory methods

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

in Java

编程语言与"静态"的含义有关,但与"工厂"的定义无关。


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

例如,在数据库连接类中,您可能只需要一个类来创建数据库连接,这样,如果您决定从MySQL切换到Oracle,您只需在一个类中更改逻辑,应用程序的其余部分将使用新连接。

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

它保护应用程序的其余部分不受您可能对工厂所做的更改的影响,而这正是您的目的。

它之所以是静态的,是因为如果您想要跟踪一些有限的资源(套接字连接或文件句柄的数量),那么这个类可以跟踪有多少资源已经被传出并返回,所以您不会耗尽有限的资源。


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

The ability of static factory methods to return the same object from
repeated invocations allows classes to maintain strict control over
what instances exist at any time. Classes that do this are said to be
instance-controlled. There are several reasons to write
instance-controlled classes. Instance control allows a class to
guarantee that it is a singleton (Item 3) or noninstantiable (Item 4).
Also, it allows an immutable class (Item 15) to make the guarantee
that no two equal instances exist: a.equals(b) if and only if a==b. If
a class makes this guarantee, then its clients can use the == operator
instead of the equals(Object) method, which may result in improved
performance. Enum types (Item 30) provide this guarantee.

从有效Java,Joshua Bloch(项目1,第6页)