什么是静态工厂方法?

时间:2009-05-30 04:46:16

标签: java design-patterns factory-method

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

14 个答案:

答案 0 :(得分:445)

static factory method pattern是一种封装对象创建的方法。如果没有工厂方法,您只需直接调用类constructorFoo x = new Foo()。使用此模式,您将调用工厂方法:Foo x = Foo.create()。构造函数被标记为私有,因此除了从类内部调用它们之外不能调用它们,并且工厂方法被标记为static,因此可以在没有第一个对象的情况下调用它。

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

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

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

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

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

这也可用于提高可读性,正如Rasmus所说。

答案 1 :(得分:153)

请注意! “静态工厂方法 工厂方法模式相同”(c)Effective Java,Joshua Bloch。

工厂方法:“定义用于创建对象的接口,但让实现接口的类决定实例化哪个类.Plant方法允许类将实例化延迟到子类”(c)GoF。

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

区别:

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

  • valueOf
  • getInstance
  • 的newInstance

答案 2 :(得分:112)

我们避免提供对数据库连接的直接访问,因为它们是资源密集型的。因此,如果我们低于限制,我们使用静态工厂方法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);
     //...
   }
}

答案 3 :(得分:67)

可通过静态工厂方法改进可读性:

比较

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

答案 4 :(得分:20)

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

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

答案 5 :(得分:18)

这一切都归结为可维护性。放置它的最佳方法是,只要您使用new关键字创建对象,就可以将要写入的代码与实现相结合。

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

今天,工厂基本上已被抛弃,转而使用依赖注入,因为它们需要大量的锅炉板代码,而这些代码结果有点难以维护。依赖注入基本上等同于工厂,但允许您指定对象如何以声明方式(通过配置或注释)连接在一起。

答案 6 :(得分:10)

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

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

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

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

所以现在你可以从它外面为这个类创建一个对象。喜欢的方式......

Test t = Test.getObject();

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

答案 7 :(得分:8)

我想我会根据我所知道的内容为这篇文章添加一些亮点。我们在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 methodsconstructors不同,它们可以返回其返回类型的任何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;
    }
}

答案 8 :(得分:4)

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

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

答案 9 :(得分:3)

源于静态工厂的一个优点是API可以在不将其类公开的情况下返回对象。这导致非常紧凑的API。在java中,这是通过Collections类实现的,它隐藏了大约32个类,这使得它的集合API非常紧凑。

答案 10 :(得分:1)

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

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

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

它可以保护应用程序的其余部分免受您对工厂所做的更改,这就是目的。

它是静态的原因是如果你想跟踪一些有限的资源(套接字连接或文件句柄的数量),那么这个类可以跟踪传递和返回的数量,所以你不要耗尽有限的资源。

答案 11 :(得分:1)

  

静态

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

  

工厂方法

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

  Java中的

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

答案 12 :(得分:1)

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

java.lang.String 类还有静态工厂方法

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

答案 13 :(得分:0)

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

  

静态工厂方法从中返回相同对象的能力   重复调用允许类保持严格控制   什么实例随时存在。据说这样做的类   实例的控制。写作有几个原因   实例控制的类。实例控件允许类   保证它是单例(第3项)或不可实例化(第4项)。   此外,它允许不可变类(第15项)作出保证   没有两个相等的实例存在:a.equals(b)当且仅当a == b。如果   一个类做出这个保证,然后它的客户端可以使用==运算符   而不是equals(Object)方法,这可能会导致改进   性能。枚举类型(第30项)提供此保证。

来自Effective Java,Joshua Bloch(第1项,第6页)