Constructores vs Métodos de fábrica [cerrado]


181

Al modelar clases, cuál es la forma preferida de inicializar:

  1. Constructores, o
  2. Métodos de fábrica

¿Y cuáles serían las consideraciones para usar cualquiera de ellos?

En ciertas situaciones, prefiero tener un método de fábrica que devuelva nulo si el objeto no se puede construir. Esto hace que el código sea ordenado. Simplemente puedo verificar si el valor devuelto no es nulo antes de tomar una acción alternativa, en contraste con lanzar una excepción del constructor. (Personalmente no me gustan las excepciones)

Digamos que tengo un constructor en una clase que espera un valor de id. El constructor usa este valor para completar la clase desde la base de datos. En el caso de que no exista un registro con la identificación especificada, el constructor lanza una excepción RecordNotFoundException. En este caso, tendré que incluir la construcción de todas esas clases dentro de un bloque try..catch.

En contraste con esto, puedo tener un método de fábrica estático en esas clases que devolverá nulo si no se encuentra el registro.

¿Qué enfoque es mejor en este caso, constructor o método de fábrica?

Respuestas:


66

De la página 108 de Patrones de diseño: elementos de software orientado a objetos reutilizables por Gamma, Helm, Johnson y Vlissides.

Use el patrón Método de fábrica cuando

  • una clase no puede anticipar la clase de objetos que debe crear
  • una clase quiere que sus subclases especifiquen los objetos que crea
  • las clases delegan la responsabilidad a una de varias subclases auxiliares, y desea localizar el conocimiento de qué subclase auxiliar es el delegado

21
El método de fábrica estático es diferente del patrón de diseño GoF: Patrón de método de fábrica. stackoverflow.com/questions/929021/…
Sree Rama

No compare con el patrón del método Factory de los patrones de diseño GoF.
Sree Rama

137
esto no me explica nada
Sushant

@Sushant, ¿por qué es así?
PaulD

2
Esta respuesta no responde la pregunta, solo transmite información para leer para comprender / explicar el concepto ... Esto es más un comentario.
Crt

202

Pregúntese qué son y por qué los tenemos. Ambos están allí para crear una instancia de un objeto.

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

No hay diferencia hasta ahora. Ahora imagine que tenemos varios tipos de escuelas y queremos cambiar de usar ElementarySchool a HighSchool (que se deriva de una ElementarySchool o implementa la misma interfaz ISchool que la ElementarySchool). El cambio de código sería:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

En caso de una interfaz tendríamos:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

Ahora, si tiene este código en varios lugares, puede ver que usar el método de fábrica puede ser bastante barato porque una vez que cambia el método de fábrica ya está listo (si usamos el segundo ejemplo con interfaces).

Y esta es la principal diferencia y ventaja. Cuando comienza a tratar con jerarquías de clase complejas y desea crear dinámicamente una instancia de una clase a partir de dicha jerarquía, obtiene el siguiente código. Los métodos de fábrica pueden tomar un parámetro que le dice al método qué instancia concreta se debe instanciar. Digamos que tiene una clase MyStudent y necesita crear una instancia del objeto ISchool correspondiente para que su estudiante sea miembro de esa escuela.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Ahora tiene un lugar en su aplicación que contiene la lógica de negocios que determina qué objeto de ISchool instanciar para diferentes objetos de IStudent.

Entonces, para las clases simples (objetos de valor, etc.), el constructor está bien (no desea modificar la ingeniería de su aplicación), pero para las jerarquías de clases complejas, el método de fábrica es una forma preferida.

De esta manera, sigue el primer principio de diseño de la pandilla de cuatro libros "Programa para una interfaz, no una implementación".


2
Incluso cuando piensa que es una clase simple, existe la posibilidad de que alguien necesite extender su clase simple, por lo que el método de fábrica es aún mejor. Por ejemplo, puede comenzar con ElementarySchool pero luego alguien (incluido usted) puede ampliarlo con PrivateElementarySchool y PublicElementarySchool.
Jack

10
esta debería ser la respuesta aceptada
am05mhz

2
@David, buena respuesta, pero ¿puedes ampliar un ejemplo donde cada implementación de interfaz puede requerir diferentes parámetros para la construcción? Aquí hay un ejemplo tonto: ¿ IFood sandwich = new Sandwich(Cheese chz, Meat meat);y IFood soup = new Soup(Broth broth, Vegetable veg);cómo pueden ayudar aquí la fábrica o el constructor?
Brian

1
Acabo de leer otras tres explicaciones sobre el propósito del uso de fábrica, y esta es la que finalmente me hizo "clic". ¡Gracias!
Daniel Peirano

¿Por qué no se acepta esta respuesta?
Tomás

74

Debe leer (si tiene acceso a) Java 2 Elemento 1 efectivo : considere métodos de fábrica estáticos en lugar de constructores .

Ventajas de los métodos estáticos de fábrica:

  1. Tienen nombres.
  2. No están obligados a crear un nuevo objeto cada vez que se invocan.
  3. Pueden devolver un objeto de cualquier subtipo de su tipo de retorno.
  4. Reducen la verbosidad de la creación de instancias de tipo parametrizadas.

Desventajas de los métodos estáticos de fábrica:

  1. Al proporcionar solo métodos de fábrica estáticos, las clases sin constructores públicos o protegidos no se pueden subclasificar.
  2. No se distinguen fácilmente de otros métodos estáticos.

44
Esto me parece un error grave en Java, luego un problema general de OOD. Existen numerosos lenguajes OO que ni siquiera tienen constructores, sin embargo, la subclasificación funciona bien.
Jörg W Mittag

1
@cherouvim por qué la mayoría del código se escribe utilizando Constructores si( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Asif Mushtaq

Buenos puntos. Sin embargo, es específico de Java. Se puede presentar un caso para una característica de lenguaje que hace que los métodos de fábrica sean distinguibles de otros métodos estáticos.
OCDev

30

Por defecto, los constructores deben ser preferidos, porque son más simples de entender y escribir. Sin embargo, si necesita desacoplar específicamente las sutilezas de construcción de un objeto de su significado semántico tal como lo entiende el código del cliente, sería mejor usar fábricas.

La diferencia entre constructores y fábricas es análoga a, por ejemplo, una variable y un puntero a una variable. Hay otro nivel de indirección, que es una desventaja; pero también hay otro nivel de flexibilidad, que es una ventaja. Entonces, al hacer una elección, le recomendamos que haga este análisis de costo versus beneficio.


17
Entonces, (estilo TDD) comenzaría con los constructores como la forma más simple de hacer el trabajo. ¿Y luego refactorizar a las fábricas una vez que comience a obtener olores de código (como lógica condicional repetida que determina a qué constructor llamar)?
AndyM

1
Punto muy importante. Un estudio de usuarios que comparó fábricas y constructores encontró resultados muy significativos que indican que las fábricas son perjudiciales para la usabilidad API: "los usuarios requieren significativamente más tiempo (p = 0.005) para construir un objeto con una fábrica que con un constructor" [The Factory Pattern in API Design : Una evaluación de usabilidad ].
mdeff

12

Use una fábrica solo cuando necesite control adicional con la creación de objetos, de una manera que no se podría hacer con los constructores.

Las fábricas tienen la posibilidad de almacenar en caché, por ejemplo.

Otra forma de usar fábricas es en un escenario en el que no sabes el tipo que quieres construir. A menudo ve este tipo de uso en escenarios de fábrica de complementos, donde cada complemento debe derivar de una clase base o implementar algún tipo de interfaz. La fábrica crea instancias de clases que derivan de la clase base o que implementan la interfaz.


11

Una cita de "Java eficaz", 2ª ed., Ítem 1: Considere métodos de fábrica estáticos en lugar de constructores, p. 5:

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


10

Además de "Java efectivo" (como se menciona en otra respuesta), otro libro clásico también sugiere:

Prefiere los métodos estáticos de fábrica (con nombres que describen los argumentos) a los constructores sobrecargados.

P.ej. no escribas

Complex complex = new Complex(23.0);

pero en cambio escribe

Complex complex = Complex.fromRealNumber(23.0);

El libro llega a sugerir que el Complex(float)constructor sea privado, para obligar al usuario a llamar al método estático de fábrica.


2
Leer esa parte del libro me trajo aquí
Purple Haze

1
@Bayrem: yo también, lo estaba releyendo recientemente y pensé que debería agregarlo a las respuestas.
blue_note

1
En una nota relacionada, que pueden ser útiles algunas convenciones de nombres resueltos por el java.time marco con respecto a la denominación de from…, to…, parse…, with…, y así sucesivamente. Tenga en cuenta que las clases java.time están diseñadas para ser inmutables, pero algunas de estas convenciones de nomenclatura también pueden ser útiles para seguir con clases mutables.
Basil Bourque

7

Un ejemplo concreto de una aplicación CAD / CAM.

Se haría un camino de corte utilizando un constructor. Es una serie de líneas y arcos que definen un camino para cortar. Si bien la serie de líneas y arcos puede ser diferente y tener diferentes coordenadas, se maneja fácilmente pasando una lista a un constructor.

Una forma sería hecha usando una fábrica. Porque si bien hay una clase de forma, cada forma se configurará de manera diferente según el tipo de forma que sea. No sabemos qué forma vamos a inicializar hasta que el usuario realice una selección.


5

Digamos que tengo un constructor en una clase que espera un valor de id. El constructor usa este valor para completar la clase desde la base de datos.

Este proceso definitivamente debería estar fuera de un constructor.

  1. El constructor no debe acceder a la base de datos.

  2. La tarea y la razón de un constructor es inicializar miembros de datos y establecer invariantes de clase utilizando valores pasados ​​al constructor.

  3. Para todo lo demás, un mejor enfoque es utilizar un método de fábrica estático o, en casos más complejos, una clase de fábrica o de constructor separada .

Algunas líneas de guía del constructor de Microsoft :

Haz un trabajo mínimo en el constructor. Los constructores no deberían hacer mucho trabajo más que capturar los parámetros del constructor. El costo de cualquier otro procesamiento debe retrasarse hasta que se requiera.

Y

Considere utilizar un método de fábrica estático en lugar de un constructor si la semántica de la operación deseada no se asigna directamente a la construcción de una nueva instancia.


2

A veces tiene que verificar / calcular algunos valores / condiciones mientras crea un objeto. Y si puede lanzar una excepción, el constructro es muy malo. Entonces necesitas hacer algo como esto:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

Donde todos los cálculos adicionales están en init (). Pero solo usted como desarrollador realmente sabe acerca de este init (). Y, por supuesto, después de meses te olvidas de eso. Pero si tiene una fábrica, solo haga todo lo que necesita en un método para ocultar este init () de la llamada directa, así que no hay problemas. Con este enfoque no hay problemas con caer en la creación y la pérdida de memoria.

Alguien te contó sobre el almacenamiento en caché. Es bueno. Pero también debe recordar el patrón Flyweight, que es agradable de usar con la forma Factory.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.