我正在阅读Joshua Bloch的“Effective Java Programming Language Guide”。
他解释了静态工厂方法可以用来避免不必要的重复对象。
我还没完全明白这一点。
任何人都可以解释吗?静态工厂避免重复对象的方法
回答
一个真实的例子:
的Java支持基本的Object类型来表示一个字节。当转换到原始对象,你可以这样做:
Byte b = new Byte((byte) 65);
对接,这将在每次调用创建一个新的实例。相反,你这样做:
Byte b = Byte.valueOf((byte) 65);
每次调用,该方法的valueOf()将返回一个字节对象代表的字节值65
10000电话后第一例将创造10000点的对象相同的实例,而第二个只有一个,因为Byte类有一个Byte对象的内部缓存,代表-128到127之间的所有数字。
当你调用一个构造函数时,它总会返回一个新的对象(除非抛出异常)。静态工厂方法或任何类型的工厂都不必总是返回一个新对象。例如,传统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
我希望这些例子有所帮助。
您能否提供代码片段以便更好地理解?谢谢! – 2009-09-13 03:30:18
我试过:-) – SingleShot 2009-09-13 03:44:21
factory method pattern对于不需要创建对象的新实例以执行某些操作的时间很有用。
这里有一对夫妇的一般情况下,我能想到的地方,它返回相同对象的静态工厂方法就可以派上用场:
目的是昂贵的创建 - 有一个当对象被实例化时很多处理,因此不止一次实例化对象是不可取的。 (这也与Singleton pattern)
的对象保持无状态 - 如果在实例之间没有状态差,没有好的目的,以创建新的对象每次。
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;
}
}
(仅供参考,上述代码是单的一个例子同样,它不是线程安全。 )
如果你有一个工厂类来创建对象实例,每次你去创建一个对象,你也必须实例化工厂类。基本上你会创建这个工厂类的重复。
如果它是静态的,则只有工厂的一个实例被使用。
不,这不是关于工厂,而是关于它创建的对象。 – 2009-09-13 03:31:27
我能阅读这本书的一些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;
}
}
好吧,我无法从书中理解缓存部分,也许以后我可以问这个问题。 – 2009-09-13 03:41:44
呃...缓存节省了你创建一个你真正不想要的东西的新实例的开销。在这种情况下,将呼叫者交回同一个实例是可以的,因为状态并不重要。一个例子是您在代码中使用的服务。该服务为您提供了一项功能。您可以通过将呼叫者交回相同的对象来节省创建新实例的开销 – 2009-09-13 03:55:24
所有非重复的答案似乎把重点放在单件模式,这是不可复制的一个很好的例子,但一个坏的模式在一般情况下使用。在我看来,一个给定的应用程序应该有零个到一个单例,并且优先为零。但是,这与不创建不必要的对象无关。
请考虑一个必须制作大量Date对象的应用程序。它制作了很多Date对象,以致Date对象的构造对性能产生不利影响。因此,改为调用Date对象的构造函数,将代码重构为仅通过工厂方法创建日期。在这个工厂方法中,检查一个Map来查看请求的日期是否已经被创建。如果是这样,那么从Map返回相同的对象。否则,创建一个新的,放入地图并返回。
什么似乎混淆你是如何通过调用工厂方法,防止创建一个重复的对象。仅仅通过调用工厂方法并不能改变任何事情。调用工厂允许的是代码接管并作出关于创建对象的决定。拨打新电话时,不能做出这样的决定。
另请参阅this question了解该模式及其用途。
是的,我也这么认为。关于辛格尔顿的好点! – 2009-09-13 04:36:57
日期是一个不好的例子,因为它是(通过糟糕的设计)可变的,它真的不应该是一个瓶颈(除非我错过了某些东西)。 – 2009-09-13 10:00:46
@Tom,在标准API中为true。我正在考虑一个JODA时间。 – Yishai 2009-09-13 15:29:09
如果我没记错的话,他也会在书中给出一个例子。考虑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对象。相反,您可以使用工厂方法完全控制您创建的实例数量。这就是为什么它被称为工厂。
- 1. 工厂类与对象初始化 - 试图避免静态
- 2. 静态工厂方法和静态对象的内存泄漏
- 3. 避免与factory_boy工厂重复使用
- 4. 静态工厂方法vs工厂
- 5. AutoMapper静态工厂方法
- 6. 静态工厂方法(Spring)
- 7. 静态工厂方法
- 8. Autofac工厂静态方法
- 9. 静态工厂方法
- 10. 静态工厂方法
- 11. 当创建一种工厂时避免静态方法的可测试性
- 12. 如何通过静态工厂方法返回对象?
- 13. 如何使用静态工厂方法创建对象?
- 14. 创建一个从静态工厂方法工厂,然后调用静态工厂方法上的ID
- 15. 工厂方法VS工厂对象
- 16. 避免主要方法静态定义?
- 17. EJB的静态工厂方法
- 18. 避免重复对象初始值
- 19. 避免与数据对象重复
- 20. Spring 3 @Component和静态工厂方法
- 21. 静态工厂方法查询
- 22. 继承和静态工厂方法
- 23. 静态接口工厂方法[Java 8]
- 24. 静态工厂方法问题!
- 25. Java 8接口 - 静态工厂方法
- 26. 避免键盘重复的方法?
- 27. mysql - 避免重复的最好方法
- 28. 如何避免重复代码的静态多态性
- 29. 工厂方法模式,以避免基于条件逻辑实例化对象
- 30. Java中的抽象静态工厂方法[getInstance()]?
它将通过“缓存”的初始化创建256,或者如果缓存已经初始化,则为none。没有基于实例进行初始化。 – 2009-09-13 10:02:16
实际上,缓存的初始化将创建256个实例。这是因为在预先填充缓存的Byte类中有一个静态初始化程序。显然,开发人员认为预先填充缓存比懒惰初始化效率更高。 但是,这不是静态工厂方法的重要特征。它可以以任何方式实施。 – idrosid 2009-09-13 10:19:40