2008-10-11 16 views
41

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?Métodos de fábrica estáticos vs Instancia (normal) constructores?

Por ejemplo, si va a crear un String de un char[]:

  1. String.FromCharacters(chars);

  2. new String(chars);

+5

Han agregado "método" al título, ya que de lo contrario sugiere que la elección se realiza entre un constructor de instancias o un constructor estático. (El texto es claro, pero el título provocó una cierta pregunta "¿Huh?" En mi mente :) –

+0

@JonSkeet Tu edición se ha deshecho :) – gcampbell

+0

@gcampbell: Lo volveré a hacer, considerando la pregunta claramente * no es * sobre constructores estáticos ... –

Respuesta

50

En Effective Java, 2nd edition, Joshua Bloch ciertamente recomienda la primera. Hay algunas razones que puedo recordar, y sin duda algunas que no puedo:

  • Puede dar un nombre significativo al método. 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 de la primera - puede tener diferentes métodos de fábrica con la misma lista de parámetros
  • Usted puede regresar nulo para los casos "potencialmente de que el fallo", mientras que un constructor le siempre o bien devolver un valor o una excepción
  • puede volver un tipo distinto a las declaradas (por ejemplo, devolver una clase derivada)
  • se puede utilizar como una fábrica, a potencialmente devolver una referencia al mismo objeto varias veces

los inconvenientes:

  • no es tan idiomático, en la actualidad - los desarrolladores están más acostumbrados a ver "nueva"
  • Si ve "nueva" que saben que está recibiendo una nueva instancia (módulo el oddity I mentioned recently)
  • 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 al método estático
+2

Leyendo el Artículo 2 en Java Eficaz uno puede fácilmente tener la impresión de que Joshua Bloch está a favor de usar métodos estáticos de fábrica como la opción predeterminada. Pero no creo que ese sea realmente el caso, o al menos no fue en 2002 cuando lo hizo [esta entrevista] (http://www.artima.com/intv/bloch14.html) con Bill Venners que cité en mi respuesta más abajo. –

+0

re. tu punto sobre C#, he usado este patrón en el pasado; Vea mi respuesta aquí que ofrece una solución alternativa al problema del inicializador compacto (¡bueno, lo reemplaza con algo que también es compacto de todos modos!) Http://stackoverflow.com/a/10171659/705589 –

2

Método estático. Luego puede devolver un nulo, en lugar de arrojar una excepción (a menos que sea un tipo de referencia)

2

Prefiero el constructor de instancias, porque eso tiene más sentido para mí y hay menos ambigüedad potencial con lo que está tratando de expresar (es decir: qué sucede si FromCharacters es un método que toma un solo carácter). Ciertamente subjetivo, sin embargo.

2

Personalmente, prefiero ver un constructor normal, ya que los constructores deben utilizarse para construir. Sin embargo, si hay una buena razón para usar, es decir, si FromCharacters declaró explícitamente que no asignó nueva memoria, valdría la pena. Lo "nuevo" en la invocación tiene significado.

7

Hay un documento del 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

+0

Desafortunadamente, esa es una buena respuesta. Estoy dispuesto a hacer la asignación sin la palabra clave "nueva", pero la visibilidad es muy importante. Odiaría que alguien haga su propio procesamiento para crear un objeto String teórico porque no vieron un constructor. –

+0

Acepto, el problema de cómo asegurar que las personas descubran una estática es importante. El segundo autor de ese documento está construyendo alguna herramienta para eso como parte de su trabajo de doctorado, será interesante ver dónde termina esto. – Uri

+0

@Uri, por supuesto es más lento ** actualmente ** porque todavía no hay estándares que crean convenciones de nombres adecuados para constructores estáticos. Contraste eso con la convención ampliamente conocida de 'new' en constructores normales. – Pacerier

9

Si el objeto es inmutable, es posible que pueda utilizar el método estático para devolver los objetos en caché y se ahorrará la asignación y el procesamiento de la memoria.

19

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; 
    } 
} 

Debido a que se adhieran a esta convención, si veo

Foo f = Foo.CreateFoo(); 
Bar b = new Bar(); 

al leer el código, tengo un conjunto muy diferente de las expectativas acerca de lo que cada uno de los dos líneas está haciendo. 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.

+1

Buen punto sobre el procesamiento anormal. Personalmente, no me gusta la idea de usar un método estático en lugar de un constructor predeterminado. Es repetitivo y va en contra del flujo del lenguaje. Preferiría refactorizar el procesamiento anormal y usar la nueva palabra clave, si es posible. –

+0

En mi humilde opinión debería ser la respuesta correcta. –

+0

@Robert, Esto no invalida el argumento "use static as default". Todavía puede diferenciar un constructor estático de un constructor estático especial (que debe * ver *) mediante convenciones de nomenclatura. P.ej. 'Foo.New() 'como el reemplazo predeterminado para' new', y 'Foo.FromAbc()' etc para los * especiales *. – Pacerier

2

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, ha olvidado otro caso común: un constructor nulo/predeterminado emparejado con un método de inicialización.

+0

Sí, no pensé en ese caso. Creo que es inferior a los dos presentados porque necesita líneas de código. Podría editar la pregunta para incluirla. –

+0

@ejgottl, ¿Qué quiere decir con "constructor nulo emparejado con inicialización"? – Pacerier

+0

Un constructor que no toma parámetros y, quizás, solo asigna memoria dejando el objeto sin inicializar. Emparejado con métodos de inicialización. Si recuerdo correctamente, así es como se implementan los objetos Ruby en C. – ejgottl

1

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*. El nuevo Google Collections Library hace un gran trabajo de esto, yo.

+1

¿Podría ampliar un poco en * why * 'of *' y 'for *' son mejores nombres que 'new *' o 'create *'? Nombrar las cosas correctamente es importante para mí, así que me gustaría aprender algo nuevo aquí. – stakx

+0

@stakx: Si usa * nuevo o crea *, se le "obliga" a proporcionar una nueva instancia, mientras que usar los nombres propuestos le permite reutilizar los existentes. –

7

He estado trabajando recientemente en una API pública, 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 un quote from a Bill-Venners interview with Josh Bloch que he encontrado útil:

Cuando está escribiendo una clase, puede correr por la lista del mi libro de las ventajas de fábricas estáticas durante constructores públicos. Si encuentra que se aplica una gran cantidad de esas ventajas en su caso , entonces debe ir con las fábricas estáticas . De lo contrario, debe ir con los constructores.

Algunas personas se desilusionaron al encontrar ese consejo en mi libro. Lo leyeron y dijeron: "Usted ha argumentado tan fuertemente para las fábricas públicas estáticas que nosotros deberíamos usarlas de manera predeterminada". I creo que la única desventaja real en es que es un poco desconcertante para las personas que usan para 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 en la documentación, porque Javadoc agrupa todos los constructores. Pero yo diría que todos deberían considerar fábricas estáticas todo el tiempo , y usarlas cuando son apropiadas.

Después de leer esa cita, y the study that Uri mentioned *, me siento inclinado a errar en favor de los constructores a menos que existan razones de peso para no hacerlo. 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 ...

* Lamentablemente, 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 estoy no estoy seguro de que se pueda llegar a la conclusión de que los métodos estáticos de fábrica confunden a muchos programadores. Aunque el estudio sí me dio la impresión de que a menudo lo haría.

+0

+1 para la profundidad * * ness *. Necesita más votos positivos – Pacerier

Cuestiones relacionadas