我正在读约书亚布洛赫的“Effective Java Programming Language Guide” 他解释说静态工厂方法可用于避免不必要的重复对象 我对此并不十分了解 谁能解释一下?
答案 0 :(得分:7)
一个现实世界的例子:
Java支持原始类型和对象类型来表示字节。将基元转换为对象时,可以执行以下操作:
Byte b = new Byte( (byte) 65);
这会为每次通话创建一个新实例。相反,你做:
Byte b = Byte.valueOf( (byte) 65);
在每次调用时,方法valueOf()将返回表示字节值65的Byte对象的相同实例。
在10000次调用之后,第一个示例将创建10000个对象,而第二个示例仅创建一个,因为Byte类具有Byte对象的内部缓存,表示-128到127之间的所有数字。
答案 1 :(得分:6)
关于非重复的所有答案似乎都集中在单例模式上,这是非重复的一个很好的例子,但在一般情况下使用的模式不好。在我看来,给定的应用程序中应该有零到一个单例,偏好为零。但是,这与不创建不必要的对象几乎没有关系。
相反,请考虑一个必须创建大量Date对象的应用程序。正在制作如此多的Date对象,Date对象的构造对性能产生了负面影响。因此,在调用Date对象的构造函数时,代码将被重构为仅通过工厂方法创建日期。在此工厂方法中,将检查Map以查看是否已创建所请求的日期。如果是,则从Map返回相同的对象。否则创建一个新的,放入Map并返回。
让您感到困惑的是如何通过调用工厂方法来阻止创建重复对象。只是通过调用工厂方法并没有真正改变任何东西。调用工厂允许的是代码接管并做出关于创建对象的决定。在打电话给新人时,不能做出这样的决定。
另请参阅this question以了解有关模式及其可用性的更多信息。
答案 2 :(得分: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
我希望这些例子有所帮助。
答案 3 :(得分:2)
factory method pattern对于无需创建对象的新实例以执行某些操作的时间非常有用。
以下是一些我能想到的一般情况:返回同一对象的静态工厂方法可以派上用场:
创建对象的成本很高 - 实例化对象时会有很多处理,因此不希望多次实例化对象。 (这也与Singleton pattern有关。)
对象不保持状态 - 如果实例之间没有状态差异,则每次创建新对象都没有好处。
Wikipedia page on the factory method pattern有关于此主题的更多信息。
让我们来看一个具体的例子。
DateFormat
类使用getInstance
静态方法返回DateFormat
实例,该实例可用于将Date
格式化为预设格式,具体取决于机器。
由于返回的DateFormat
对每个日期格式化操作使用相同的格式,因此没有真正的理由每次都创建新的DateFormat
实例。
通常,实现它的方法是在实例尚不存在的情况下创建实例,然后保留对该实例的引用。如果再次需要实例,则返回引用。 (这通常也是单例模式的实现方式。)
例如:
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;
}
}
(仅供参考,以上代码是单例的一个示例。此外,它不是线程安全的。)
答案 4 :(得分:2)
如果我记得很清楚,他也会在书中给出一个例子。考虑Decimal
。经常使用零。因此,如果您要调用静态工厂方法Decimal.valueOf("0")
(不知道这是否是实际的API,但这对于此示例而言无关紧要),它将返回一个Decimal表示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对象。相反,您可以使用工厂方法完全控制您创建的实例数。这就是为什么它被称为工厂。
答案 5 :(得分: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;
}
}
答案 6 :(得分:-1)
如果您有一个工厂类来创建对象实例化,那么每次创建对象时,您都必须实例化工厂类。基本上你会创建这个工厂类的重复。
如果是静态的,则只能使用工厂的一个实例。