2010-09-29 7 views
12

Siempre he sentido que, en general, el trabajo principal de una clase debe hacerse en sus métodos de instancia, mientras que el constructor solo debe obtener la instancia en un estado inicial utilizable.¿Algún problema con hacer el trabajo principal de una clase en su constructor?

Pero me parece que en la práctica hay situaciones en las que parece tener más sentido colocar esencialmente todo el trabajo real en el constructor.

Un ejemplo: necesito recuperar información específica de DBMS de la base de datos. La forma más natural para mí parecía tener un DBMSSpecInfo clase, con un constructor:

public DBMSSpecInfo(java.sql.Connection conn) throws SQLException{ 
    // ... retrieve info from DBMS 
} 

/** @returns max size of table in kiB */ 
public int getMaxTableSize() {//...} 

/** @returns max size of index in kiB */ 
public int getMaxIndexSize() {//...} 

/** @returns name of default schema */ 
public String getDefaultSchema() {//...} 

Se podría construir la clase una vez, el constructor se vendería todos los datos, entonces se podría utilizar diversos métodos get para recuperar la información que necesita .

Por supuesto que podría poner el método en otro lugar, y solo usar DBMSSpecInfo para el valor de retorno (esencialmente usando DBMSSpecInfo solo como portador del valor), pero se siente feo crear una clase solo para devolver valores desde una sola función.

¿Qué opinas? ¿Hay problemas con la realización del trabajo principal en el constructor? ¿Es "no idiomático" en Java? ¿O es una práctica aceptable (aunque posiblemente poco común)?

Respuesta

4

En su ejemplo me gustaría hacer un método estático GetDBMSSpecInfo (java.sql.Connection conn) que devolverá una instancia de DBMSSpecInfo objeto o null si algo va mal (en caso de que no quieren tirar excepciones).

El objeto DBMSSpecInfo para mí no deben contener nada más que obtener propiedades: MaxIndexSize, MaxTableSize, DefaultSchema, etc.

Y me gustaría hacer el constructor de este objeto privado para que los casos sólo se pueden crear a partir de la estática método.

+0

+1 De hecho, esto es exactamente lo que hice. Acabo de hacer público el constructor de DBMSSpecInfo, pero como es solo un valor containert (solo 'public''fans' fields + constructor) no hay daño en que otros construyan instancias. Además, un constructor público ayuda si necesita instancias con valores predeterminados para probar otras unidades de otros métodos. – sleske

+0

Este es el compromiso más razonable: no destruye la encapsulación, pero aún permite pruebas de unidades relativamente sencillas. Pero todavía creo que las cosas deberían ser burladas. –

+0

Sí, como mencionas si necesitas instancias con valores predeterminados para pruebas unitarias, está bien tener un constructor público. Pero esto también tiene que ver con la funcionalidad que desea aplicar. Hay muchos casos en los que desea asegurarse de que se haya creado una determinada instancia de objeto con toda la información correcta. Piense, por ejemplo, DataRow. En ese caso, recomendaría encarecidamente constructores privados o internos. – Dummy01

10

El principal problema práctico son las pruebas unitarias: no podrá crear instancias del objeto sin realizar el trabajo real. (O tendría que burlarse de todas las clases que participan en este trabajo).

Discusión relacionada: OO Design for testability. Da ejemplos de por qué hacer trabajo en constructores es malo para las pruebas unitarias.

+0

+1 punto muy bueno, perdido por todos los demás que responden (incluyéndome a mí). –

+0

Buen punto. Sin embargo, si el constructor es la * única * parte que sí funciona, el punto es discutible. – sleske

+0

Así que supongo que la lección sería: Haga su trabajo en el constructor o en el (los) método (s) de instancia, pero no en ambos (ya que entonces no puede probar cada parte individualmente). – sleske

0

En mi opinión, el constructor debe ser ligero y no debe arrojar excepciones. Implementaré algún tipo de método Load() para recuperar datos de la base de datos o implementar la carga diferida.

+5

Hm, un método 'Load()' no implicaría una construcción en dos pasos (primer constructo, luego carga()). Creo firmemente que la construcción en dos pasos es mala (además, no puedes hacer que la clase sea inmutable). – sleske

+1

¿Podría corroborar su opinión con una fundación? –

+0

No, no siempre implicaría una construcción en dos pasos, tal vez no necesita el acceso a la base de datos. Evitaría cosas costosas dentro del constructor porque puede que no sea necesario cada vez que instales la clase. – elsni

6

Preferiría separar el código de creación de la clase en esos casos. Se puede poner en un método de fábrica estático o en una clase de fábrica separada (que también puede ser una clase interna estática pública). La elección depende de la complejidad del código y el contexto del diseño (que no conocemos en este caso).

Esto también le permitiría realizar optimizaciones, como el almacenamiento en caché y la reutilización de la (s) instancia (s) de clase.

+0

Buen punto. Quizás debería crear una clase separat "holder". – sleske

5

Soy grande en pragmatismo. Si funciona, ¡hazlo! Pero en nombre de la pureza y la bondad, me gustaría hacer una sugerencia de diseño:

Esta clase confunde el contenido de datos con el mecanismo para recuperarlo. El objeto que termina usando en otro lugar es interesante solo para los datos que contiene. Entonces, lo "limpio" sería tener una clase diferente para extraer la información y luego crear instancias de este objeto de propiedades.

Esa otra clase podría tener una vida útil más larga, ya que normalmente estarías llamando a un método para hacer el trabajo, no al constructor. El constructor de DBMSSpecInfo podría terminar asignando un conjunto de propiedades pero sin hacer un montón de trabajo de acceso a DB con capacidad de error.

+0

+1 parece Péter Török y usted acepta :-) – sleske

3

No creo que sea una buena idea hacer el trabajo principal en un constructor, ya que no tiene un valor de retorno. Por lo tanto, hace que el procesamiento de errores sea más complicado, ya que lo obliga a usar excepciones.

+4

Creo firmemente que, salvo circunstancias muy inusuales, el procesamiento de errores debería * siempre * usar excepciones. Así que realmente consideraría esto una ventaja :-). – sleske

2

Una desventaja de hacer el trabajo en el constructor es que los constructores no pueden ser anulados (ni deberían delegar en métodos invalidables).

Otra es que un constructor es todo o nada. Si el objeto contiene datos cuyas inicializaciones muestran fallas independientes, se priva de la capacidad de usar qué datos se pueden obtener con éxito. Del mismo modo, si tiene que inicializar todo el objeto, incluso si solo necesita una parte, podría afectar negativamente el rendimiento.

Por otro lado, hacerlo en el constructor permite que el estado de inicialización (aquí: la conexión a la base de datos) se comparta y se libere antes.

Como siempre, diferentes enfoques son preferibles en diferentes circunstancias.

+0

Quizás. OHOH es probable que la consulta SQL realizada en el constructor en el ejemplo del OP pueda hacerse en una única consulta SQL, que sería un escenario de falla o nada falla. –

+0

Estoy de acuerdo. Pero para otros ejemplos, pueden ser un factor decisivo, y me gusta evitar la generalización excesiva de los consejos de diseño cuando puedo. – meriton

2

Hacer todo el trabajo en el constructor puede conducir a "infierno de sobrecarga". Sigue queriendo agregar más funciones y en lugar de simplemente agregar un nuevo método, como lo haría en el desarrollo normal orientado a objetos, se encontrará agregando más y más constructores sobrecargados. Eventualmente, los constructores pueden crecer tantas sobrecargas y parámetros que se vuelve difícil de manejar.

+0

+1 Todavía no estoy en ese punto, pero es algo a tener en cuenta. – sleske

0

No hay problema. JDK tiene muchas clases que hacen IO de red en constructores.

+0

Hm, el hecho de que las clases JDK lo hagan no significa que sea una buena práctica. Algunos incluso argumentan que es todo lo contrario ...: -/ – sleske

+0

como qué? Yo diría que es un mal diseño hacer IO en un constructor. ¿Puedes señalar una clase jdk que hace esto? – bsautner

+0

@bsautner: Un ejemplo es 'java.net.Socket', donde algunos constructores inician una conexión. Sin embargo, también hay un constructor que no hace esto, por lo que puede elegir. – sleske

1

Solo tenga cuidado de que el objeto no esté clonado/deserializado. Las instancias creadas de esta manera no usan el constructor.

+0

Bueno, nunca he serializado un objeto, y creo que 'clone' es * evil *. Aún así, es bueno tener esto en cuenta ... – sleske

Cuestiones relacionadas