2010-03-09 19 views
5

Mi despachador está "seleccionando" el controlador correcto; luego se crea la instancia del Controlador (DependencyInjectionContainer se pasa al constructor del Controlador); a continuación, llamar al método de algún controlador ...MVC, DI (inyección de dependencia) y creación de instancia de modelo desde el controlador

class UserController extends Controller 
{ 

    public function __construct(DependencyInjectionContainer $injection) { 
    $this->container = $injection; 
    } 

    public function detailsAction() { 
    ... 
    } 

} 

DependencyInjectionContainer contiene adaptador DB objeto, objeto de configuración, etc. Ahora vamos a ver lo que() método contiene detailsAction ...

public function detailsAction() { 

    $model = new UserModel(); 
    $model->getDetails(12345); 

} 

Como se ve I' m creando una nueva instancia de UserModel y llamando a los métodos getDetails. El método getDetails() del modelo debe conectarse a db para obtener información sobre el usuario. Para conectarse a DB, UserModel debe poder acceder al adaptador DB.

¿Cuál es la forma correcta de pasar DependencyInjectionContainer al UserModel? Creo que de esta manera está mal ...

public function detailsAction() { 

    $model = new UserModel($this->container); 
    $model->getDetails(12345); 

} 
+0

Esto es esencialmente un duplicado de esto: http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it -trough-t –

+0

Acabo de leer esto http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it -through-t y este http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2/1475861#1475861 pero todavía estoy confundido. Entonces, ¿debería pasar DependencyInjectionObject a cualquier instancia nueva por cadena (de una a otra)? – Kirzilla

+0

@Mark Seemann Wow, ¡has escrito todo el libro sobre Dependency Injection! Maldición, DI me parece cada vez más difícil con cada pregunta :) – Kirzilla

Respuesta

10

En lugar de inyectar todo el DI Container en sus clases, debe inyectar solo las dependencias que necesite.

Su UserController requiere un adaptador de base de datos (vamos a llamar a esta interfaz IDBAdapter). En C# esto podría tener este aspecto:

public class UserController 
{ 
    private readonly IDBAdapter db; 

    public UserController(IDBAdapter db) 
    { 
     if (db == null) 
     { 
      throw new ArgumentNullException("db"); 
     } 

     this.db = db; 
    } 

    public void DetailsAction() 
    { 
     var model = new UserModel(this.db); 
     model.GetDetails(12345); 
    } 
} 

En este caso estamos injectiing la dependencia en el UserModel.En la mayoría de los casos, sin embargo, tendería a considerar un olor DI si el UserController solo toma una dependencia para pasarlo, por lo que un mejor enfoque podría ser que UserController dependa de una fábrica abstracta como esta:

public interface IUserModelFactory 
{ 
    UserModel Create(); 
} 

en esta variación, el UserController podría tener este aspecto:

public class UserController 
{ 
    private readonly IUserModelFactory factory; 

    public UserController(IUserModelFactory factory) 
    { 
     if (factory == null) 
     { 
      throw new ArgumentNullException("factory"); 
     } 

     this.factory = factory; 
    } 

    public void DetailsAction() 
    { 
     var model = this.factory.Create(); 
     model.GetDetails(12345); 
    } 
} 

y se podía definir un UserModelFactory concreta que se lleva a una dependencia de IDBAdapter:

public class UserModelFactory : IUserModelFactory 
{ 
    private readonly IDBAdapter db; 

    public UserModelFactory(IDBAdapter db) 
    { 
     if (db == null) 
     { 
      throw new ArgumentNullException("db"); 
     } 

     this.db = db; 
    } 

    public UserModel Create() 
    { 
     return new UserModel(this.db); 
    } 
} 

Esto le da mejor separación de preocupaciones.

Si necesita más de una dependencia, simplemente inyéctelas a través del constructor. Cuando comienza a obtener demasiados, es una señal de que está violando el Single Responsibility Principle, y es hora de refactor to Aggregate Services.

+0

@Mark Seemann, ¡gracias por su respuesta! Lo leeré algunas veces para entender las ventajas. – Kirzilla

+0

Veo la única desventaja de su método (imho). ¿Qué sucede si estoy llamando al método UserModel que no necesita ninguna conexión con DB? Todos los objetos de inyección de dependencias que se pasan al constructor UserModel ya se están creando en UserModelFactory. (Todo esto es sobre el rendimiento). ¿Qué piensas de esta realización de DIContainer? http://twittee.org/ – Kirzilla

+0

Si el modelo de usuario no utiliza la base de datos en todos los casos, es posible que desee considerar si debe tener una dependencia de ella en absoluto.En cualquier caso, no resuelve ningún problema al inyectar el contenedor en lugar de dependencias específicas, y generalmente no compro el argumento sobre el rendimiento, porque los costosos servicios (como el adaptador DB) tienden a compartirse entre muchos consumidores, y incluso si este no es el caso, hay formas de lidiar con eso: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx –

0

que haría uso de un objeto único para todos los parámetros de configuración: Se configura en su arranque, y luego optar por utilizar directamente o pasarlo como parámetro en tus objetos

La idea es tener un método completo para recuperar sus datos de configuración.

Puede proporcionar una clase abstracta para la manipulación de db que utiliza su configuración. semifallo. DependancyInjection aún se puede usar para anular sus datos predeterminados.

El enlace de arriba en el comentario (posible 'duplicado') concluye sobre el uso de la inyección de constructor: está cerca de su método actual.

Sin embargo, si trato de entender cómo funciona su modelo, supongo que tendrá muchas otras clases de modelos además de "userModel". Por lo tanto, una clase abstracta que use un singleton de configuración podría ser una buena solución: todas las próximas clases de modelo ampliarán esta clase abstracta y no tendrá que preocuparse por su configuración.

Por otro lado, su solución es buena para mí siempre que su dependendanceInjectionContainer cambie con frecuencia.

+0

Hablando francamente, los objetos en mi dependendanceInjectionContainer no cambian con frecuencia. Estoy almacenando DB Adapter, Memcache Adapter, Mail Adapter y Config en dependanceInjectionContainer. Entonces, probablemente, algún tipo de Registro está bien para mí. No puedo ni imaginar la situación en la que debería cambiar algo en dependdanceInjectionContainer (¿alguna situación de la vida real?) – Kirzilla

+0

En lo que respecta a mi humilde experiencia, aún no he visto ningún uso de DI para un controlador. ¿Tal vez si necesita hacer malabarismos con múltiples bases de datos? o si pasa algunos parámetros específicos del usuario (proporcionando un tema por ejemplo) – Rodolphe

+0

Por cierto, puedo hacer que dependanceInjectionContainer sea Singleton y obtener su instancia en Controller o Model. Me ayudará a evitar el paso de dependanceInjectionContainer al constructor. Entonces, esperemos a que el Sr. Mark Seemann responda para ver cuál es la clave ... – Kirzilla

Cuestiones relacionadas