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

评论

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

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

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

#1 楼

我们避免直接访问数据库连接,因为它们占用大量资源。因此,我们使用静态工厂方法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)中?

–张海峰
2014年5月6日17:19

@haifzhan,这并不是真的要完整,但可以。

–马修·弗拉申(Matthew Flaschen)
2014年5月7日,0:46

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

–滚石
2015年6月4日在8:46

@Sridhar,不,它是存在的连接数(已跟踪(因此不会创建超过MAX_CONNS的连接数)),而不是正在循环的连接数。

–马修·弗拉申(Matthew Flaschen)
2015年6月10日在6:15

如果DbConnection为Closeable,则@narFlaschen you方法将被sonarqube ad阻止程序错误捕获。

–阿旺·比鲁(Awan Biru)
18年9月3日,下午3:37

#2 楼

静态工厂方法模式是封装对象创建的一种方法。如果没有工厂方法,则只需直接调用类的构造函数:Foo x = new Foo()。使用此模式,您可以改为调用工厂方法: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指出的那样,它也可以用来提高可读性。

评论


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

–乔什·阳光(Josh Sunshine)
13年1月14日在15:40

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

–user656698
2013年6月1日13:10

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

–西奥多·默多克(Theodore Murdock)
2014年9月17日下午0:38

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

–emeraldhieu
2014-09-19 17:05



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

–凯文
15年1月7日,下午5:01

#3 楼

注意! “静态工厂方法与工厂方法模式不同”(c)有效的Java,Joshua Bloch。

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

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

区别:

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


valueOf
getInstance
newInstance


评论


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

–沉
18年5月11日15:47

@Shen通常,当您拥有诸如newInstance之类的静态方法时,构造函数是私有的,因此您无法使用new A()创建对象,在这种情况下,请将newInstance方法设为static和public并随其返回新的A()对象。附言我知道我迟到了。

–Shubhi Dimri
7月14日22:31

#4 楼

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

比较

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年8月8日在9:10

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

–拉斯穆斯·法伯(Rasmus Faber)
16年8月8日在12:01



我认为这不是很好。如果您有三个或三个以上的参数,如何使用该想法并为该方法创建一个漂亮的名称?

– Dherik
16年11月29日在14:26

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

–拉斯穆斯·法伯(Rasmus Faber)
16年11月29日在21:02

#5 楼



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



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

#6 楼

归结为可维护性。最好的解决方法是,每当您使用new关键字创建对象时,便会将正在编写的代码耦合到实现上。根据您对对象所做的操作当使用构造函数创建所有对象时,实际上是在将使用该对象的代码硬连线到该实现。使用您的对象的代码“依赖于”该对象。从表面上看,这似乎没什么大不了,但是当对象改变时(想改变构造函数的签名,或子类化对象),则必须返回并重新连接所有地方。

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

评论


您是说工厂需要纾困吗? ;)

–约翰·法雷尔(John Farrell)
09年5月30日在6:07

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

–cwash
09年5月30日在6:11

#7 楼

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

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


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

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


所以现在您可以为此类创建对象从外面。像这样...

Test t = Test.getObject();


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

评论


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

– Ojonugwa Jude Ochalifu
18年6月1日在12:58

更新。不管怎么说,还是要谢谢你

–山多士
18年6月5日在5:08

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

–沉
18年11月25日在9:29

#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实例。

来自有效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;
    }
}


#9 楼

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

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

评论


假设您的工厂返回一个接口类型,而不是要处理的具体类。

–比尔·林奇
09年5月30日在5:28

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

–乔什·阳光(Josh Sunshine)
13年1月14日在15:38

#10 楼

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

#11 楼

当您要确保只有一个实例将返回要使用的具体类时,使用静态工厂方法是很好的。例如,在数据库连接类中,您可能只想拥有一个实例。一类创建数据库连接,因此,如果您决定从Mysql切换到Oracle,则只需更改一类中的逻辑,其余的应用程序将使用新的连接。

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

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

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

#12 楼

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


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


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

#13 楼


静态


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


工厂方法


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


Java中的编程语言


编程语言与“静态”的含义相关,但而不是“工厂”的定义。

评论


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

–user207421
2014年6月9日在2:39



我说过,因为答案太短了,在第一眼,第二眼和第三眼的眼神中确实不太了解,所以反正要由OP提出。没关系,我已经改变主意并尝试删除下降表决,但我不能。

–维克多
2014年6月9日14:49

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

–user207421
2014年8月11日,1:16

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

–维克多
2014年8月12日15:07



@Victor Rubbish。事实不是主观的,也不可以从事实或公理推论得出的问题。另一方面,“太短”是主观的,我已经解决了。这个答案本质上与您未评论的答案相同。您的评论仍然晦涩难懂。

–user207421
2015年5月19日在9:54

#14 楼

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


Arrays.asList("1","2","3")
Collections.synchronizedList(..), Collections.emptyList(), Collections.unmodifiableList(...) (仅某些示例,可以检查javadocs中的mor方法示例https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html)

java.lang也.String类具有此类静态工厂方法:


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