design-patterns - pattern - patron builder
¿Métodos de fábrica estáticos frente a constructores de instancias(normales)? (10)
En un idioma en el que ambos estén disponibles, ¿preferiría ver un constructor de instancias o un método estático que devuelva una instancia?
Por ejemplo, si está creando un String
de un char[]
:
String.FromCharacters(chars);
new String(chars);
Como Jon Skeet parafraseó a Josh Bloch, hay una serie de razones por las que un método de fábrica estático es preferible a un constructor en muchos casos. Diría que si la clase es simple, sin configuración costosa o uso complicado, quédese con el constructor idiomático. Las JVM modernas hacen que la creación de objetos sea extremadamente rápida y económica. Si la clase puede estar subclasificada o puede hacer que sea inmutable (una gran ventaja para la programación concurrente, que solo va a ser más importante), entonces vaya con el método de fábrica.
Un consejo más No nombre el método de fábrica Foo.new*
o Foo.create*
. Un método con estos nombres siempre debe devolver una nueva instancia, y al hacerlo pierde una de las grandes ventajas del método de fábrica. Una mejor convención de nombres es Foo.of*
o Foo.for*
. La nueva Biblioteca de colecciones de Google hace un gran trabajo al respecto.
Depende. Para los idiomas en los que usar un constructor de instancia es "normal", generalmente usaría uno a menos que tuviera buenas razones para no hacerlo. Esto sigue el principio de la menor sorpresa.
Por cierto, olvidaste otro caso común: un constructor nulo / predeterminado emparejado con un método de inicialización.
En Effective Java, 2nd edition , Joshua Bloch ciertamente recomienda lo primero. Hay algunas razones que puedo recordar, y sin duda algunas que no puedo:
- Puedes darle al método un nombre significativo. Si tiene dos formas de construir una instancia que toman una int, pero tienen diferentes significados para esa int, el uso de un método normal hace que el código de llamada sea mucho más legible.
- Un corolario del primero: puede tener diferentes métodos de fábrica con la misma lista de parámetros
- Puede devolver nulo para casos de "fallas potencialmente esperadas" mientras que un constructor siempre devolverá un valor o arrojará una excepción
- Puede devolver un tipo distinto al declarado (por ejemplo, devolver una clase derivada)
- Puede usarlo como una fábrica, para devolver potencialmente una referencia al mismo objeto varias veces
Las desventajas:
- No es tan idiomático, actualmente - los desarrolladores están más acostumbrados a ver "nuevo"
- Si ve "nuevo", sabrá que está obteniendo una nueva instancia (módulo de la rareza que mencioné recientemente )
- Necesita hacer que los constructores apropiados estén disponibles para las subclases
- En C # 3, las llamadas de constructor pueden establecer campos / propiedades de forma compacta con expresiones de inicializador de objetos; la función no se aplica a las llamadas a métodos estáticos
Escribo un constructor cuando la creación de la instancia no tiene efectos secundarios, es decir, cuando lo único que el constructor está haciendo es inicializando propiedades. Escribo un método estático (y hago que el constructor sea privado) si la creación de la instancia hace algo que normalmente no esperarías que hiciera un constructor.
Por ejemplo:
public class Foo
{
private Foo() { }
private static List<Foo> FooList = new List<Foo>();
public static Foo CreateFoo()
{
Foo f = new Foo();
FooList.Add(f);
return f;
}
}
Porque me adhiero a esta convención, si veo
Foo f = Foo.CreateFoo();
Bar b = new Bar();
mientras leo mi código, tengo un conjunto muy diferente de expectativas sobre lo que está haciendo cada una de esas dos líneas. Ese código no me dice qué es lo que hace que crear un Foo sea diferente de crear un Bar, pero me dice que tengo que mirar.
Hay un documento de ICSE''07 que estudia la usabilidad de los constructores frente a los patrones de fábrica. Si bien prefiero los patrones de fábrica, el estudio mostró que los desarrolladores fueron más lentos en encontrar el método de fábrica correcto.
http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf
He estado trabajando en una API pública recientemente, y he estado angustiado por la elección de los métodos de fábrica estáticos frente al constructor. Los métodos de fábrica estáticos definitivamente tienen sentido en algunos casos, pero en otros no es tan claro y no estoy seguro de si la coherencia con el resto de la API es razón suficiente para incluirlos sobre los constructores.
De todos modos, me encontré con una cita de una entrevista de Bill-Venners con Josh Bloch que encontré útil:
Cuando estás escribiendo una clase, puedes ver la lista de las ventajas de las fábricas estáticas sobre los constructores públicos de mi libro. Si encuentra que una cantidad significativa de esas ventajas se aplican realmente en su caso, entonces debe ir con las fábricas estáticas. De lo contrario, debería ir con los constructores.
Algunas personas se decepcionaron al encontrar ese consejo en mi libro. Lo leyeron y dijeron: "Ustedes han argumentado tan fuertemente para las fábricas públicas estáticas que deberíamos usarlas por defecto". Creo que la única desventaja real al hacerlo es que es un poco desconcertante para las personas que están acostumbradas a usar constructores para crear sus objetos. Y supongo que proporciona un poco menos de una señal visual en el programa. (No se ve la nueva palabra clave.) También es un poco más difícil encontrar fábricas estáticas en la documentación, porque Javadoc agrupa a todos los constructores. Pero yo diría que todos deberían considerar las fábricas estáticas todo el tiempo, y usarlas cuando sean apropiadas.
Después de leer esa cita y el estudio que Uri mencionó *, me siento inclinado a equivocarme a favor de los constructores, a menos que existan razones de peso para hacer lo contrario. Creo que un método de fábrica estático sin una buena causa probablemente sea una complejidad innecesaria y una sobreingeniería. Aunque podría cambiar de opinión otra vez mañana ...
* Desafortunadamente, este estudio se centró menos en los métodos de fábrica estáticos y más en el patrón de fábrica (donde existe un objeto de fábrica separado para crear nuevas instancias), así que no estoy seguro de que uno pueda llegar a la conclusión de que los métodos de fábrica estáticos confunden a muchos programadores. Aunque el estudio sí me dio la impresión de que a menudo lo haría.
Método estático. Luego puede devolver un valor nulo, en lugar de arrojar una excepción (a menos que sea un tipo de referencia)
Personalmente prefiero ver un constructor normal, ya que los contructors deberían usarse para construir. Sin embargo, si hay una buena razón para no usar uno, es decir, si FromCharacters declaró explícitamente que no asignó nueva memoria, valdría la pena. Lo "nuevo" en la invocación tiene significado.
Prefiero el constructor de instancia, simplemente porque eso tiene más sentido para mí, y hay menos ambigüedad potencial con lo que estás tratando de expresar (es decir: ¿qué pasa si FromCharacters es un método que toma un solo carácter). Ciertamente subjetivo, sin embargo.
Si su objeto es inmutable, puede usar el método estático para devolver objetos en caché y ahorrarse la asignación y el procesamiento de la memoria.