2010-10-26 8 views
9

Estoy haciendo el diseño de un proyecto pequeño donde no usé programación contra interfaces para todas mis clases. Descubrí que algunas clases casi nunca serían necesarias para cambiar, así que dejo que las clases de sus clientes las mencionen como clases concretas.¿Debo hacer explícitas las dependencias de clases concretas mediante la inyección de constructores en mis clases?

tanto, vamos a decir que tenemos ClassB que va a ser consumida ser consumido por ClassA:

alt text

class ClassB { 
} 

Mi pregunta es, ¿debería crear ClassB en ClassA, o debería pasar esa responsabilidad "arriba" en la jerarquía? Voy a represento ambos casos:

class ClassA { 
    private ClassB classB; 

    public ClassA() { 
     this.classB = new ClassB(); 
    } 
} 

o

class ClassA { 
    private ClassB classB; 

    public ClassA(ClassB classB) { 
     this.classB = classB; 
    } 
} 

Me gustaría saber cómo lo haría y żqué ser la razón detrás de él!

Gracias

+1

hace la diferencia! ¡Todavía estás programando para el concreto! –

+0

En realidad lo hace. Puedo pasar las clases extendidas de ClassB. Pero entonces quizás tenga más sentido simplemente programar ClassA contra una interfaz en primer lugar. :( –

+0

Sí .................. –

Respuesta

3

Algunas de las ventajas de su primera opción (A crea B):

  1. El usuario de la clase A tiene que saber nada acerca de B o cómo crear & configurarlo. Esto lo convierte en un modelo de programación más simple.
  2. B también se puede hacer privado/interno, por lo que los usuarios de su biblioteca no necesitan pasar por muchas clases de ayuda que no necesitan ver.
  3. Puede cambiar la forma en que A funciona para no usar B sin romper el código de llamada.
  4. encapsulación - nadie llega a interferir con B desde el exterior mientras que A está funcionando

Algunas de las ventajas de su segunda opción (A toma una dependencia de B):

  1. El usuario de la clase A tiene control total sobre el objeto B, pueden configurarlo de manera diferente, pueden pasar el mismo en varias instancias de A si lo desean.
  2. Abre la puerta para que se pasen las diferentes implementaciones (si B es una interfaz o clase base) que es de gran valor para las pruebas unitarias o la extensibilidad.
  3. actualización: si en el futuro, se hace más complicado crear B (por ejemplo B tiene algunas dependencias de su propia), a continuación, que se puede manejar fuera sin necesidad de realizar cambios en A.

Tenga en cuenta que también es posible crear una "mejor de ambos mundos" solución mediante el uso de lo que se llama a veces "la inyección de dependencia de los pobres":

class ClassA { 
    private ClassB classB; 

    public ClassA(ClassB classB) { 
     this.classB = classB; 
    } 

    public ClassA() : this(new ClassB()) { 

    } 
} 
3

Depende de la aplicación, no hay una respuesta general. Si A tiene todos los datos y conocimientos relevantes para inicializar B, entonces podría inicializarlo.

Si no desea que A sepa cómo inicializar B o si no desea dar a A todos los datos para la inicialización, debe crearlo externamente.

+0

Parece una buena heurística a seguir. –

2

es mejor que inserte la interfaz que implementó ClassB.

class ClassA { 
private InterfaceB B; 

public ClassA(InterfaceB B) { 
    this.B = B; 
} 
} 
class classB: InterfaceB 
{ 
} 

Tenga en cuenta que InterfaceB puede ser una clase regular como clase base para sus derivados queridos. En este caso, todavía funciona como interfaz.

+4

Sí, pero creo que te estás perdiendo mi punto. No quiero hinchar mi código con básicamente 1 interfaz para cada clase concreta si no voy a necesitar tanta flexibilidad. Tal vez estoy equivocado pensando de esta manera? Solo intento equilibrar la "complejidad" y flexibilidad del proyecto. –

+0

@devoured bien cuando su inyección no hace ninguna diferencia. Sería lo mismo que classB es un miembro privado. La idea de DI es ser independiente de la realización concreta de las clases dependientes. – Arseny

+0

Usar una interfaz en lugar de una clase concreta no hará explotar la complejidad de su implementación. Pero te permitirá ser más flexible. De esta forma, no crea dependencia entre las clases, lo cual es un infierno para refactorizar después. – Arthis

1

En el anterior A asume la total responsabilidad de la instanciación de clase B. Entonces, no hay nada que pueda salir mal. A sabe B como cualquier cosa. En el caso A, se deben establecer pocas propiedades de B antes de invocar cualquier método. Puede hacerlo estableciendo esos justo después de la inicialización.

En este último, A no puede estar tan seguro de B.

Ahora, la elección es, por supuesto, suya.

0

un par de ventajas para la segunda opción (pasando el objeto a través de la constructor, es decir, control de inversión) que no se han mencionado hasta ahora:

1) Puede utilizar una herramienta de inyección de dependencias para inyectar el objeto classB. Aunque no puede inyectar implementaciones diferentes porque está bloqueado a la clase concreta, puede especificar el ciclo de vida de los objetos (singleton, por solicitud, por subproceso) y cualquier parámetro de constructor utilizado para crear una instancia del objeto.

2) Si siempre pasa dependencias en el constructor (inversión de control), entonces es más claro qué dependencias tiene cada clase de un vistazo.

Cuestiones relacionadas