2010-03-24 14 views
9

Estoy comenzando un nuevo proyecto y estableciendo la base para trabajar. Han surgido algunas preguntas y es probable que le esté haciendo algunas preguntas aquí, espero encontrar algunas respuestas.Acceso al contenedor DI

El primer paso es controlar las dependencias de los objetos. Decidí ir con el patrón de diseño de inyección de dependencia, del cual soy algo nuevo, para manejar todo esto para la aplicación.

Cuando realmente lo codificando me encontré con un problema. Si una clase tiene múltiples dependencias y desea pasar múltiples dependencias a través del constructor (para que no puedan ser modificadas después de crear una instancia del objeto).

¿Cómo lo haces sin pasar una serie de dependencias, usando call_user_func_array(), eval() o Reflection? Esto es lo que estoy buscando:

<?php 

class DI 
{ 
    public function getClass($classname) 
    { 
     if(!$this->pool[$classname]) { 
      # Load dependencies 
      $deps = $this->loadDependencies($classname); 

      # Here is where the magic should happen 
      $instance = new $classname($dep1, $dep2, $dep3); 

      # Add to pool 
      $this->pool[$classname] = $instance; 

      return $instance; 
     } else { 
       return $this->pool[$classname]; 
     } 
    } 
} 

Una vez más, me gustaría evitar los métodos más costosos para llamar a la clase. ¿Cualquier otra sugerencia?

Además, ¿cómo puedo acceder a la clase DI dentro de las clases, por ejemplo, en los controladores que necesitan acceder a diferentes modelos? ¿Debo llamarlo estáticamente o pasarlo a lo largo de cada clase que lo requiera? No creo que la última idea sea factible.

Gracias a todos.

Respuesta

21

[Antes de comenzar, permítanme decir que soy principalmente un programador de Java, con solo un poco de conocimiento de PHP. Pero yo simplemente voy a tratar de conseguir los conceptos más importantes de todo y sin características específicas del lenguaje]

inyección de dependencia se basa en dos partes de código:.

  1. construcción
  2. ejecución

En su forma más extrema, no hay operadores new que se encuentran en la parte Ejecución. Todos ellos se mueven a la parte de Construcción. (En la práctica, esto se atenuará.)

Toda la construcción ocurre - en la parte de Construcción. Crea el gráfico de los objetos necesarios para la ejecución de abajo hacia arriba. Así que vamos a suponer, se debe construir un:

  • A depende de B, y
  • B depende de C.

Entonces

  • C se construye primero.
  • Luego B se construye con C como parámetro.
  • Entonces A se construye con B como parámetro.

So C no tiene que pasar como un parámetro de constructor a A. Este pequeño ejemplo no ilustra con suficiente fuerza, cuánto reduce la cantidad de objetos que se tienen que pasar a un tamaño bastante pequeño número.

El inyector de dependencia en sí no se debe pasar a la parte de ejecución. Este es uno de los errores básicos que todos (incluido yo mismo) intentamos hacer, cuando entran en contacto por primera vez con DI. El problema es que esto borrará completamente las líneas entre Construcción y Ejecución. Otra forma de decirlo es que violaría el Law of Demeter. O en el patrón de hablar: eventualmente "degradaría" el patrón de Inyección de Dependencia al patrón del Localizador de Servicios. Es discutible, si esto es realmente una degradación, pero en cualquier caso por lo general no es una buena idea hacer un mal uso del Inyector de Dependencia como un Localizador de Servicio.

Por lo tanto, cuando necesite dar a uno de sus objetos construidos la capacidad de producir otros objetos durante la ejecución, en lugar de pasar el Inyector de Dependencia, solo pasaría proveedores simples (un término utilizado por el marco Java DI Guice). Estas son clases bastante simples que solo pueden crear un cierto tipo de objeto. Tienen similitudes con una fábrica.

Primero intente pasar las dependencias requeridas directamente al constructor.

lo tanto, para resumir:

  • construir objetos de abajo hacia arriba.
  • Solo pase las pocas dependencias necesarias para crear un objeto.
  • Una vez que haya terminado, comience a ejecutar.
  • Durante la ejecución, aún puede obtener objetos recién creados utilizando Proveedores.

Pero no lo tome demasiado lejos: Objetos simples todavía se pueden crear sin un Proveedor :-)

Y ahora, todo lo que tiene que hacer es traducir esto en un código de calidad. Tal vez otros pueden ayudarte con algunos ejemplos de PHP.

Adición: Un poco más sobre proveedores de

Como se señaló anteriormente, la noción de "Proveedor" (una fábrica especializada) es un poco específica al marco de Java DI Guice. Este marco puede crear automáticamente un proveedor para cualquier tipo de objeto. Sin embargo, el concepto es generalmente útil para DI. La única diferencia es que sin la ayuda de Guice o un marco similar, tendrá que escribir los Proveedores usted mismo, pero eso es bastante fácil:

Digamos que B depende de C.

  • Si B sólo necesita una instancia fijo de C, entonces no es necesario un Proveedor - simplemente puede construir B con el argumento del constructor C.
  • Si B necesita para crear más instancias de C durante la ejecución , simplemente escriba una clase llamada CProvider con un método get(), que pueda crear una nueva instancia de C. Luego pase una instancia de CProvider en el constructor de B y almacene el proveedor en un campo de instancia de B. Ahora B puede llamar al cProvider.get() cuando necesita una nueva instancia de C.

Los proveedores son parte del código de construcción, por lo que puede usar new C(...)! Por otro lado, no son parte del código de ejecución, por lo que no debería haber ninguna lógica de ejecución allí.

CProvider se pueden pasar a múltiples constructores, por supuesto. También puede escribir múltiples versiones CProvider1, CProvider2, ... - donde cada uno puede construir diferentes versiones de objetos C con diferentes propiedades. O simple instancia de CProvider varias veces con diferentes argumentos.

+0

Perspicaz, gracias por la explicación. Sé sobre el patrón de Localizador de Servicio y eso no es lo que estoy tratando de lograr. ¿Podría explicar un poco sobre el uso de los proveedores? – Andre

+0

Agregó un apéndice. Hágame saber si necesita más información. –

+0

Muchas gracias por la ayuda. Básicamente, necesito pasar los proveedores necesarios para cada dependencia de múltiples instancias. Estos son básicamente patrones de fábrica pero específicos para crear objetos para un propósito (podría ser una variedad de clases, un ejemplo serían los controladores de salida) y devolverlos a la clase dependiente. Parece más complejidad entrante: D – Andre

2

Debe considerar usar un contenedor IOC para administrar sus dependencias por usted. Un buen contenedor de COI debería encargarse de pasar las dependencias entre constructores dependientes por usted.

Existe un question que pregunta sobre las opciones de contenedor de IOC para PHP.

2

Parece que está intentando rodar su propio contenedor de inyección de dependencia. ¿Por qué no utilizar uno que ya existe, como Symfony, Crafty o Sphicy?

+0

El objetivo es evitar el uso de bibliotecas de terceros a menos que sea absolutamente necesario (principalmente debido a problemas de licencia). – Andre

+0

@andre: Symfony está licenciado bajo la licencia de MIT. Léelo, básicamente dice "haz lo que quieras": http://www.opensource.org/licenses/mit-license.php –

+2

Estoy creando mi propia licencia y no quiero atribuirla a bibliotecas de terceros. o software (parte de los requisitos del proyecto), como tal, necesito crear mi propio contenedor. – Andre

Cuestiones relacionadas