2009-09-13 71 views

回答

7

一个真实的例子:

的Java支持基本的Object类型来表示一个字节。当转换到原始对象,你可以这样做:

Byte b = new Byte((byte) 65); 

对接,这将在每次调用创建一个新的实例。相反,你这样做:

Byte b = Byte.valueOf((byte) 65); 

每次调用,该方法的valueOf()将返回一个字节对象代表的字节值65

10000电话后第一例将创造10000点的对象相同的实例,而第二个只有一个,因为Byte类有一个Byte对象的内部缓存,代表-128到127之间的所有数字。

+0

它将通过“缓存”的初始化创建256,或者如果缓存已经初始化,则为none。没有基于实例进行初始化。 – 2009-09-13 10:02:16

+0

实际上,缓存的初始化将创建256个实例。这是因为在预先填充缓存的Byte类中有一个静态初始化程序。显然,开发人员认为预先填充缓存比懒惰初始化效率更高。 但是,这不是静态工厂方法的重要特征。它可以以任何方式实施。 – idrosid 2009-09-13 10:19:40

5

当你调用一个构造函数时,它总会返回一个新的对象(除非抛出异常)。静态工厂方法或任何类型的工厂都不必总是返回一个新对象。例如,传统Singleton设计模式上的getInstance()方法是一种总是返回完全相同对象的工厂方法。在某些情况下,你有时想要做这种事情,无论是强制一个对象只能被实例化一次,还是创建某种对象池等。一般来说,我认为这是一个附带原因静态工厂方法。主要目的是创建精美的伪构造函数。

下面是一个使用静态工厂方法来制作精美的伪构造函数的例子(有点愚蠢)。考虑这个类:

class Person { 

    public Person(Role role) { 
     setRole(role); 
    } 

    ... 
} 

没有静态工厂方法,你可能会做这样的事情:

Person employee = new Person(Role.EMPLOYEE); 
Person manager = new Person(Role.MANAGER); 

相反,你可以创建静态工厂方法:

class Person { 

    public static Person newEmployee() { 
     return new Person(Role.EMPLOYEE); 
    } 

    public static Person newManager() { 
     return new Person(Role.MANAGER); 
    } 

    private Person(Role role) { 
     setRole(role); 
    } 

    ... 
} 

,你可能反而做像这样:

Person employee = Person.newEmployee(); 
Person manager = Person.newManager(); 

这可能不是一个好例子,但考虑一个更复杂的构造函数或一个描述性较弱的参数。有时使用工厂方法路线会使代码更清晰。当然也有缺点......

至于限制对象的创建,考虑到一些奇怪的约束像永远不会有一个以上的CEO:

class Person { 

    private static Person singletonCEO = new Person(Role.CEO); 

    public static Person newCEO() { 
     return singletonCEO; 
    } 

    ... 
} 

以及它将如何被创建:

Person ceo1 = Person.newCEO(); 
Person ceo2 = Person.newCEO(); 

assertThat(ceo1, is(ceo2)); // JUnit 4.x 

我希望这些例子有所帮助。

+0

您能否提供代码片段以便更好地理解?谢谢! – 2009-09-13 03:30:18

+1

我试过:-) – SingleShot 2009-09-13 03:44:21

2

factory method pattern对于不需要创建对象的新实例以执行某些操作的时间很有用。

这里有一对夫妇的一般情况下,我能想到的地方,它返回相同对象的静态工厂方法就可以派上用场:

  1. 目的是昂贵的创建 - 有一个当对象被实例化时很多处理,因此不止一次实例化对象是不可取的。 (这也与Singleton pattern

  2. 的对象保持无状态 - 如果在实例之间没有状态差,没有好的目的,以创建新的对象每次。

Wikipedia page on the factory method pattern有关于此主题的更多信息。


让我们来看一个具体的例子。

DateFormat类使用getInstance静态方法来返回DateFormat实例,其可用于根据机器的区域设置到Date格式化成预设格式。

由于返回的DateFormat对每个日期格式化操作都使用相同的格式,因此每次都没有真正的原因创建新的DateFormat实例。

通常,实现的方式是在实例尚不存在的情况下创建实例,然后保留对该实例的引用。如果再次需要实例,则返回引用。 (这是Singleton模式通常如何实现的为好。)

例如:

class MySingleInstanceObject { 

    private MySingleInstanceObject instance; 

    private MySingleInstanceObject() { 
    // Initialize the object. 
    // This may be expensive. 
    } 

    public MySingleInstanceObject getInstance() { 
    if (instance == null) { 
     instance = new MySingleInstanceObject(); 
    } 

    return instance; 
    } 
} 

(仅供参考,上述代码是单的一个例子同样,它不是线程安全。 )

+0

如果多次调用工厂方法,它以什么方式确保只返回一个对象?它是否持有对该对象的引用并将其检查为空? – 2009-09-13 03:39:27

+1

是的。在wiki上有代码示例:http://en.wikipedia.org/wiki/Singleton_pattern – jimyi 2009-09-13 03:45:10

+2

请注意,通过使用IoC容器(依赖注入)框架,您可以获得更大的灵活性(加上可测试性)并让它控制物体的寿命。 – TrueWill 2009-09-13 04:00:14

-1

如果你有一个工厂类来创建对象实例,每次你去创建一个对象,你也必须实例化工厂类。基本上你会创建这个工厂类的重复。

如果它是静态的,则只有工厂的一个实例被使用。

+0

不,这不是关于工厂,而是关于它创建的对象。 – 2009-09-13 03:31:27

0

我能阅读这本书的一些here。在阅读他所写的内容之后,他似乎在说静态工厂方法为开发人员提供了更大的灵活性,并且还可以让您更清楚地知道返回的内容。当你将它与一个构造函数进行对比时,构造函数可能不会提供返回内容的清晰度。另外,你可以在静态工厂方法中做缓存等工作,我认为这很吸引人。如果您需要这种级别的控制和灵活性,这种方法似乎是一种很好的方法。

如果您要使用缓存,则不会创建不必要的重复对象。使用这种静态工厂方法,您可以在每次调用时将相同的对象返回到静态工厂方法。

一个例子:

public class Person 
{ 

    private Person(string firstName, string lastName) 
    { 
     this.FirstName = firstName; 
     this.LastName = lastName; 
    } 

    public string FirstName {get; private set;} 
    public string LastName {get; private set;} 

    private static Dictionary<string, Person> objectPool = new Dictionary<string, Person>(); 
    private object lockObject = new object(); 

    public static Person CreatePerson(string firstName, string lastName) 
    { 
     var result = objectPool[firstName + lastName]; 
     Person person = null; 
     if (result != null) 
     { 
     return result 
     } 
     lock(lockObject) 
     { 
      person = new Person(firstName, lastName); 
      objectPool.Add(firstName + lastName, person) 
     } 
     return person; 
    } 
} 
+0

好吧,我无法从书中理解缓存部分,也许以后我可以问这个问题。 – 2009-09-13 03:41:44

+1

呃...缓存节省了你创建一个你真正不想要的东西的新实例的开销。在这种情况下,将呼叫者交回同一个实例是可以的,因为状态并不重要。一个例子是您在代码中使用的服务。该服务为您提供了一项功能。您可以通过将呼叫者交回相同的对象来节省创建新实例的开销 – 2009-09-13 03:55:24

6

所有非重复的答案似乎把重点放在单件模式,这是不可复制的一个很好的例子,但一个坏的模式在一般情况下使用。在我看来,一个给定的应用程序应该有零个到一个单例,并且优先为零。但是,这与不创建不必要的对象无关。

请考虑一个必须制作大量Date对象的应用程序。它制作了很多Date对象,以致Date对象的构造对性能产生不利影响。因此,改为调用Date对象的构造函数,将代码重构为仅通过工厂方法创建日期。在这个工厂方法中,检查一个Map来查看请求的日期是否已经被创建。如果是这样,那么从Map返回相同的对象。否则,创建一个新的,放入地图并返回。

什么似乎混淆你是如何通过调用工厂方法,防止创建一个重复的对象。仅仅通过调用工厂方法并不能改变任何事情。调用工厂允许的是代码接管并作出关于创建对象的决定。拨打新电话时,不能做出这样的决定。

另请参阅this question了解该模式及其用途。

+0

是的,我也这么认为。关于辛格尔顿的好点! – 2009-09-13 04:36:57

+0

日期是一个不好的例子,因为它是(通过糟糕的设计)可变的,它真的不应该是一个瓶颈(除非我错过了某些东西)。 – 2009-09-13 10:00:46

+0

@Tom,在标准API中为true。我正在考虑一个JODA时间。 – Yishai 2009-09-13 15:29:09

2

如果我没记错的话,他也会在书中给出一个例子。考虑Decimal。零经常使用。因此,如果您要调用静态工厂方法Decimal.valueOf("0")(不知道这是否是实际的API,但这对此示例无关紧要),它将返回Decimal reprezenting 0的一个实例,它将是任何呼叫的相同实例。实现将是这样的:

public class Decimal { 
    private static Decimal zero = new Decimal(0); 

    public static Decimal valueOf(String s) { 
     if (s.equals("0")) { 
      return zero; 
     } else { 
      return new Decimal(parse(s)); // or whatever 
     } 

    // rest of the class 
} 

请注意,只有一个零实例,而对于任何其他数字创建一个新的对象。此外,这与工厂方法一起工作,并且你不能用构造函数来做到这一点。布洛赫试图指出这是前者的优势。正如Yishai所说,这与Singleton并没有太大的关系。正如你所看到的,你可以有大量的Decimal对象。相反,您可以使用工厂方法完全控制您创建的实例数量。这就是为什么它被称为工厂。