2008-09-24 5 views
6

This question about unit testing best practices menciona el diseño de clases para la inyección de dependencias. Esto me hizo pensar qué significa exactamente eso.Directrices para diseñar clases para la inyección de dependencia

Habiendo comenzado a trabajar con la inversión de contenedores de control, tengo algunas ideas sobre el tema, así que déjame tirarlas contra la pared y ver qué se adhiere.

De la manera en que lo veo, hay tres tipos básicos de dependencias que un objeto puede tener.

  1. dependencia de los objetos - Un objeto real que será utilizado por la clase en cuestión. Por ejemplo LogInVerifier en LogInFormController. Estos deben ser inyectados a través del constructor. Si la clase tiene un nivel suficientemente alto como para requerir más de 4 de estos objetos en el constructor, considere romperlo o al menos usar un patrón de fábrica. También debería considerar proporcionar la dependencia con una interfaz y codificación contra la interfaz.
  2. A Configuración simple - Por ejemplo, un umbral o un período de tiempo de espera. En general, estos deben tener un valor predeterminado y establecerse a través de un generador de patrones de fábrica. También puede proporcionar sobrecargas de constructor que los configuren. Sin embargo, en la mayoría de los casos, probablemente no deberías forzar al cliente a tener que configurarlo de forma explícita.
  3. Un objeto de mensaje - Un objeto que se transfiere de una clase a otra que presumiblemente la clase receptora usa para lógica de negocios. Un ejemplo sería un objeto Usuario para una clase LogInCompleRouter. Aquí encuentro que a menudo es mejor que el mensaje no se especifique en el constructor, ya que entonces tendría que registrar la instancia del Usuario con el Contenedor IoC (haciéndolo global) o no crear una instancia del LogInCompleteRouter hasta después de que tenga una instancia de Usuario (para lo cual no podría usar DI o al menos necesitaría una dependencia explícita en el contenedor). En este caso, sería mejor pasar el objeto de mensaje solo cuando lo necesite para la llamada al método (es decir, LoginInCompleteRouter.Route (User u);).

Además, debo mencionar que no todo debe DI'ed, si usted tiene un poco simple de funcionalidad que era conveniente para factorizar a una clase de usar y tirar, es probablemente correcto para crear una instancia en el lugar. Obviamente esta es una llamada de juicio; si he encontrado conveniente para escribir una clase como

class PasswordEqualsVerifier { 
    public bool Check(string input, string actual) { return input===actual;} 
} 

que probablemente no me molestaría en dependencia de inyectarla y sólo tendría un objeto instanciar directamente dentro de un bloque usando. El corolario es que si vale la pena escribir pruebas unitarias, entonces probablemente vale la pena inyectar.

¿Qué piensan? Cualquier guía adicional o opiniones opuestas son bienvenidas.

Respuesta

1

Lo importante es intentar codificar las interfaces y que sus clases acepten instancias de esas interfaces en lugar de crear las instancias. Obviamente puedes volverte loco con esto, pero es una buena práctica general, independientemente de las pruebas unitarias o DI.

Por ejemplo, si tiene un objeto de datos Access, puede ser inclinado a escribir una base para todos los DAOs como este:

public class BaseDAO 
{ 
    public BaseDAO(String connectionURL, 
        String driverName, 
        String username, String password) 
    { 
     // use them to create a connection via JDBC, e.g. 
    } 

    protected Connection getConnection() { return connection; } 
} 

Sin embargo, sería mejor quitar esto de la clase a favor de una interfaz

public interface DatabaseConnection 
{ 
    Connection getConnection(); 
} 

public class BaseDAO 
{ 
    public BaseDAO(DatabaseConnection dbConnection) 
    { 
     this.dbConnection = dbConnection; 
    } 

    protected Connection getConnection() { return dbConnection.getConnection(); } 
} 

Ahora, puede proporcionar imlementations multilple de DatabaseConnection. Incluso ignorando las pruebas unitarias, si asumimos que estamos usando JDBC, hay dos maneras de obtener un Connection: un grupo de conexiones desde el contenedor, o directamente mediante el uso del controlador. Ahora, su código DAO no está acoplado a ninguna estrategia.

Para realizar pruebas, puede hacer una MockDatabaseConnection que se conecte a alguna implementación de JDBC con datos enlatados para probar su código.

Cuestiones relacionadas