¿Cuándo usar un constructor y cuándo usar el método getInstance () (métodos de fábrica estáticos)?

84
  1. Cuando y como debemos usar un constructor

    Foo bar = new Foo();
    
  2. Y cuándo y cómo deberíamos usar getInstance () (métodos de fábrica estáticos)

    Foo bar = Foo.getInstance();
    

¿Cuál es la diferencia entre estos dos? Siempre he usado un constructor, pero ¿cuándo debería usarlo getInstance()?

Zengr
fuente
¿Estás escribiendo la clase tú mismo? Si no es así, ¿cómo llamas que lo proporciona?
Chris B.
entonces, la implementación de la clase en sí significa que estoy implementando el patrón singleton, ¿verdad?
zengr
¿Estás llamando getInstance() o estás escribiendo un método llamado getInstance()?
Chris B.
1
Si la pregunta es sobre métodos de constructor vs estáticos de fábrica , sugiero aclarar y cambiar el título.
Pascal Thivent
@zengr: con respecto a su actualización2, esto podría deberse a que no nombró su método estático de acuerdo con la convención, que dicta que debe nombrarse Foo.newInstance(). Foo.getInstance()es una convención para obtener la instancia singleton de una clase. Debería corregir su ejemplo y utilizarlo Foo.newInstance()en su lugar.
JRL

Respuestas:

96

Todo el mundo parece centrarse en los singletons, mientras que creo que la pregunta es en realidad sobre métodos de constructor vs estáticos de fábrica .

Este es en realidad el elemento 1: considere los métodos de fábrica estáticos en lugar de los constructores de Effective Java de Joshua Bloch:

Elemento 1: considere métodos de fábrica estáticos en lugar de constructores

La forma normal de que una clase permita a un cliente obtener una instancia de sí misma es proporcionar un constructor público. Hay otra técnica que debería formar parte del conjunto de herramientas de todo programador. Una clase puede proporcionar un método de fábrica estático público , que es simplemente un método estático que devuelve una instancia de la clase. Aquí hay un ejemplo simple de Boolean(la clase primitiva en caja para el tipo primitivo boolean). Este método traduce un valor primitivo booleano en una Booleanreferencia de objeto:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

Tenga en cuenta que un método de fábrica estático no es lo mismo que el patrón del método de fábrica de Design Patterns [Gamma95, p. 107]. El método de fábrica estático descrito en este artículo no tiene equivalente directo en Patrones de diseño .

Una clase puede proporcionar a sus clientes métodos de fábrica estáticos en lugar de, o además de, constructores. Proporcionar un método de fábrica estático en lugar de un constructor público tiene ventajas y desventajas.

Ventajas (citando el libro):

  • Una ventaja de los métodos de fábrica estáticos es que, a diferencia de los constructores, tienen nombres.
  • Una segunda ventaja de los métodos de fábrica estáticos es que, a diferencia de los constructores, no es necesario que creen un nuevo objeto cada vez que se invocan.
  • Una tercera ventaja de los métodos de fábrica estáticos es que, a diferencia de los constructores, pueden devolver un objeto de cualquier subtipo de su tipo de retorno.
  • Una cuarta ventaja de los métodos de fábrica estáticos es que reducen la verbosidad de crear instancias de tipo parametrizado.

Desventajas (aún citando el libro):

  • La principal desventaja de proporcionar solo métodos de fábrica estáticos es que las clases sin constructores públicos o protegidos no se pueden subclasificar.
  • Una segunda desventaja de los métodos de fábrica estáticos es que no se distinguen fácilmente de otros métodos estáticos.
Pascal Thivent
fuente
8
El último se puede mitigar un poco. Tiendo a tener una clase interna estática llamada Factory y varios métodos newInstance en ella para que sea más claro cuando creo métodos de fábrica. Me ha salvado a la caza del tiempo en el pasado. :)
Rich Schuler
@Qberticus, la clase interna dedicada a los métodos de fábrica es una buena idea. Lo intentaré, gracias.
Dave O.3 de
Ciertamente, los constructores de clases deben permitir al menos la protectedsubclasificación, pero ¿el código del cliente obtiene alguna ventaja real al crear un nuevo objeto y llamar a su constructor, en lugar de llamar a un método de fábrica estático? Me parece que una llamada a un constructor encadenado es semánticamente un método de instancia, mientras que la secuencia "crear un objeto unificado y llamar a su constructor" es semánticamente equivalente a una llamada a un método de fábrica estático.
supercat
9

Tiene dos preguntas: ¿cuándo debo llamar a un getInstance()método y cuándo debo crear uno?

Si está decidiendo si llamar a un getInstance()método, es fácil. Solo necesita leer la documentación de la clase para saber cuándo debe llamarlo. Por ejemplo, NumberFormatproporciona un constructor y un getInstance()método; el getInstance()método le dará un archivo NumberFormat. Porque Calendar, por otro lado, el constructor está protegido. Usted tiene que llamar getInstance()para conseguir uno.

Si está decidiendo si crear un getInstance()método, debe decidir qué está tratando de lograr. O no quiere que la gente llame a su constructor (está creando un singleton o una fábrica ), o no le importa (como en el caso NumberFormatanterior, donde están inicializando algunos objetos para la conveniencia del llamador).


¿Larga historia corta? No se preocupe por crear getInstance()métodos en su propio código. Si llega el momento en que serán útiles, lo sabrás. Y, en general, si puede llamar al constructor de una clase, probablemente se supone que debe hacerlo, incluso si la clase proporciona un getInstance()método.

Chris B.
fuente
7

Los usos de los métodos getInstance:

Pero la mayoría de las veces su objeto será un simple POJO y el uso de constructores públicos es la solución más práctica y obvia.

U1: getInstance de otra clase

Para devolver una instancia de una clase diferente:

public class FooFactory {
    public static Foo getInstance() {
        return new Foo();
    }
}

NumberFormat.getInstanceLos métodos hacen esto ya que en realidad devuelven instancias de DecimalFormat.

U2: Problemas de singleton

El patrón singleton restringe muchos de los beneficios de la programación orientada a objetos. Los singleton generalmente tienen constructores privados, por lo tanto, no puede extenderlos. Como accederá a él a través de su método getInstance y no hará referencia a ninguna interfaz, no podrá cambiarlo por otra implementación.

krock
fuente
en el caso de un patrón de fábrica, otro nombre de método como 'createInstance' o 'buildInstance' encajaría mucho mejor, pero aún es un caso posible de lo que el solicitante podría querer (me suena un poco nebuloso y como una tarea)
jdehaan
6

Si puede usar ambos, entonces suena como un patrón singleton mal implementado .

Utilice la segunda opción si tiene la intención de tener una sola instancia de la clase en su sistema y luego hacer que el constructor sea privado.

Utilice el primero para permitir la construcción de varios objetos de la clase.

PERO no le dé a su clase las dos posibilidades.

Tenga cuidado de no usar en exceso los singleton, solo utilícelos si realmente solo existirá una instancia en el sistema, de lo contrario limitaría las posibilidades de reutilización de su clase en otros proyectos. Suena interesante poder llamar a getInstance desde cualquier lugar de su proyecto, pero eso deja poco claro quién es el propietario de esa instancia: nadie y / o todos. Si tiene muchos singleton en un proyecto, puede apostar a que el sistema está mal diseñado (generalmente). Los singleton deben usarse con cuidado, se aplican los mismos consejos que para las variables globales.

jdehaan
fuente
Se actualizó la pregunta, fue un poco confuso, lo siento
zengr
4
------> "Tenga cuidado de no abusar de los singletons" <------
Bart van Heukelom
1

Un caso en el que siempre prefiero una fábrica estática sobre un constructor normal es cuando sé que la construcción del objeto será lenta. Hago una inicialización simple en el constructor, pero si necesito crear algo pesado, usaré un método estático y documentaré el comportamiento.

Guu
fuente
0

Los singleton son malvados. Los problemas que he visto a su alrededor no se refieren a la reutilización o la capacidad de ampliación de un sistema (aunque pude ver cómo podría ocurrir eso), más bien que no puedo contar la cantidad de veces que he visto errores oscuros en un sistema que surgen de singletons.

Si necesita usar un singleton, asegúrese de que su alcance sea extremadamente estrecho, es decir, limite juiciosamente el número de otros objetos en su sistema que lo conocen.

rupjones
fuente