Factorias vs Constructores

  • febrero, 23 2022
  • Jorge Aguilera
  • 03:00
  • java

El tema de los constructores de objetos es prácticamente lo primero que se aprende cuando empiezas a usar el paradigma OOP (programación orientada a objetos).

Yo lo tuve que aprender en el paso de C a C plus plus (en el cual supongo que se inspiró luego Java y otros) y recuerdo que me explotó la cabeza cuando por fín más o menos lo entendí, teniendo en cuenta que además C plus plus permite la herencia múltiple, por lo que las llamadas entre constructores de la clase instanciada y las heredadas era todo un laberinto.

En Java el método que se llame como la clase es el constructor, y puedes tener más de uno simplemente definiendo diferentes parámetros de entrada:

class Person{
    Person(){}
    Person(String nombre)
    Person(String nombre, int edad)
    Person(String nombre, int edad, String pais)
    Person(String nombre, int edad, String pais, String moneda)
}

Todos hemos hecho estos constructores (y de hecho me consta que seguimos haciendolos, no me escondo) y sufrido por ello. Effective Java propone una serie de consejos para mejorar la construcción de objetos:

"Consider static factory methods instead of constructors"

Además de los constructores típicos (o en lugar de), el autor propone usar métodos estáticos que sirvan para construir los objetos. Por ejemplo:

class Person{
    public static Person newInstance(){
        return new A();
    }
    public static Person newInstanceWithName(String nombre){
        return new A(nombre);
    }
    public static Person newInstanceWithNameAndAge(String nombre, int edad){
        return new A(nombre, edad);
    }
    ...
}

Aunque nada te obliga a seguir haciendo los constructores públicos, la propuesta es hacerlos "invisibles" al resto de clases en favor de los métodos factory

TIP

a diferencia del constructor (que tiene que llamarse como la clase), los métodos pueden tener nombre. No es lo mismo "new A(arg0, arg1)" que "A.newInstanceWithNameAndAge( name, age)"

Cuando el número de parámetros en el constructor no ayuda en elegir cual de ellos usar, pero un factory método sí pude hacerlo.

Si tienes un objeto con uno o varios constructores, o si el número de parámetros para invocarlo es "grande" o "confuso", hazlos privados o mejor aún, eliminalos, y conviertélos en métodos static factory

TIP

otra ventaja es que a diferencia del constructor, que siempre crea un objeto nuevo, el método no tiene porqué.

Puedes entonces jugar con Caches o instancias construidas como static, etc que te permitan reutilizar objetos ya construidos

TIP

no estás sujeto a devolver una instancia de esa clase

Puedes devolver objetos que la extiendan, etc sin necesidad de que el que llame al método tenga que hacer casteos ni preocuparse por conversiones

TIP

flexibilidad para cambios futuros en las clases devueltas.

Derivado del comentario anterior, si el objeto sólo se puede construir a través de metódos estáticos, puedes cambiar en una versión futura el objeto devuelto sin afectar a quien lo llama. Por ejemplo, clases que se deprecan pueden dejar de ser utilizadas cambiando simplemente el factory

TIP

permite construir clases "al vuelo"

Aunque parece algo "para nota", el método factory puede usar la potencia de Java y obtener la implementación a devolver en el momento en el que se le llame (por ejemplo usando service provider framework, descargando el jar de un repositorio remoto, etc)

WARNING

si sólo tienes métodos static factory, la clase no puede contener subclases

WARNING

tienes que usar nombres de métodos claros que indiquen que son factoria

Todos los programadores, y el IDE; saben buscar el constructor de una clase pero al usar ahora métodos "no oficiales" hay que facilitar al que los va a usar el encontrarlos por lo que es bueno usar nombres de métodos reconocidos por las buenas prácticas y/o descriptivos ( como por ejemplo from, valueOf, newInstance, …​)

Follow comments at Telegram group Or subscribe to the Channel Telegram channel

2019 - 2024 | Mixed with Bootstrap | Baked with JBake v2.6.7