explicacion - inyeccion de dependencias php
Comprender los contenedores de IoC y la inyección de dependencias (1)
Avance rápido:
Escribo esto con la intención de comprender mejor la inyección de dependencia y los contenedores de IoC, pero también para poder corregir los errores y usarlos para ayudar a enseñarles a algunos de mis amigos también.
Por ahora, he intentado leer la documentación de varios frameworks (laravel, fuel, codeigniter, symfony) y encontré que había demasiados aspectos diferentes de los frameworks que necesitaba para sentirme cómodo al usarlo que decidí intentar simplemente aprenda cada una de las piezas principales individualmente por mi cuenta antes de intentar usarlas en los marcos mismos.
Pasé horas buscando en Google varios significados, revisando las respuestas de stackoverflow y leyendo varios artículos tratando de entender qué es un IoC y cómo usarlo para administrar correctamente las dependencias, y creo que entiendo lo que es en concepto, pero sigo siendo gris sobre cómo implementarlo correctamente. Creo que la mejor manera de que alguien que lea esto me ayude es dar mi comprensión actual de los contenedores de IoC y la inyección de dependencia, y luego dejar que las personas que tienen una mejor comprensión que yo señalen dónde mi comprensión es insuficiente.
Mi entendimiento:
- Una dependencia es cuando una instancia de ClassA requiere una instancia de ClassB para instanciar una nueva instancia de ClassA.
- Una inyección de dependencia es cuando ClassA pasa una instancia de ClassB, ya sea a través de un parámetro en el constructor de ClassA o a través de una función set ~ DependencyNameHere ~ (~ DependencyNameHere ~ $ param). (Esta es una de las áreas en las que no estoy completamente seguro) .
- Un contenedor IoC es una clase singleton (solo puede tener 1 instancia instanciada en un momento determinado) donde se puede registrar la forma específica de instanciar objetos de esa clase para este proyecto. Aquí hay un enlace a un ejemplo de lo que intento describir junto con la definición de clase para el contenedor IoC que he estado usando
Entonces en este punto es donde comienzo a intentar usar el contenedor IoC para escenarios más complicados. Por el momento parece que para usar el contenedor IoC, estoy limitado a una relación has-a para casi cualquier clase que quiera crear que tenga dependencias que quiera definir en el contenedor IoC. ¿Qué sucede si quiero crear una clase que hereda una clase, pero solo si la clase principal se ha creado de una manera específica, se registró en el contenedor IoC?
Entonces, por ejemplo: quiero crear una clase hija de mysqli, pero quiero registrar esta clase en el contenedor IoC para crear una instancia única con la clase padre construida de una manera que previamente he registrado en el contenedor IoC. No puedo pensar en una forma de hacer esto sin duplicar el código (y dado que este es un proyecto de aprendizaje, intento mantenerlo lo más "puro" posible). Aquí hay algunos ejemplos más de lo que intento describir.
Estas son algunas de mis preguntas:
- ¿Es lo que estoy tratando de hacer lo más posible sin romper algún principio de OOP? Sé que en c ++ podría usar memoria dinámica y un constructor de copia para lograrlo, pero no he podido encontrar ese tipo de funcionalidad en php. (Debo admitir que tengo muy poca experiencia en usar cualquiera de los otros métodos mágicos además de __construct, pero de leer y __clone, si entendí correctamente, no pude usarlo en el constructor para hacer que la clase hija sea instanciada como clon de un instancia de la clase padre).
- ¿Dónde deberían ir todas mis definiciones de clase de dependencia en relación con el IoC? (¿Mi IoC.php solo tiene un montón de require_once (''dependencyClassDefinition.php'') en la parte superior? Mi reacción intuitiva es que hay una manera mejor, pero todavía no he encontrado una)
- ¿En qué archivo debo registrar mis objetos? Actualmente haciendo todas las llamadas a IoC :: register () en el archivo IoC.php después de la definición de la clase.
- ¿Debo registrar una dependencia en el IoC antes de registrar una clase que necesita esa dependencia? Como no estoy invocando la función anónima hasta que realmente instanciar un objeto registrado en el IoC, supongo que no, pero sigue siendo una preocupación.
- ¿Hay algo más que esté pasando por alto que debería estar haciendo o usando? Estoy intentando dar un paso a la vez, pero tampoco quiero saber si mi código será reutilizable y, lo más importante, si alguien que no sabe nada sobre mi proyecto puede leerlo y entenderlo.
Sé que esto es extremadamente largo, y solo quería dar las gracias de antemano a cualquiera que se haya tomado el tiempo para leerlo, y aún más para que cualquiera comparta su conocimiento.
En pocas palabras (porque no es un problema limitado al mundo OOP solamente), una dependencia es una situación donde el componente A necesita (depende de) el componente B para hacer las cosas que se supone que debe hacer. La palabra también se usa para describir el componente dependiente en este escenario. Para poner esto en términos de OOP / PHP, considere el siguiente ejemplo con la analogía del coche obligatorio:
class Car {
public function start() {
$engine = new Engine();
$engine->vroom();
}
}
Car
depende del Engine
. Engine
es la dependencia del Car
. Este código es bastante malo, porque:
- la dependencia es implícita; usted no sabe que está allí hasta que inspeccione el código del
Car
- las clases están estrechamente conectadas; no puede sustituir el
Engine
conMockEngine
para fines de prueba oTurboEngine
que amplía el original sin modificar elCar
. - Parece un poco tonto que un auto sea capaz de construir un motor, ¿no?
La inyección de dependencia es una forma de resolver todos estos problemas al hacer que el Engine
necesite explícitamente Engine
y proporcionarle uno:
class Car {
protected $engine;
public function __construct(Engine $engine) {
$this->engine = $engine;
}
public function start() {
$this->engine->vroom();
}
}
$engine = new SuperDuperTurboEnginePlus(); // a subclass of Engine
$car = new Car($engine);
Lo anterior es un ejemplo de inyección de constructor , en el que la dependencia (el objeto dependiente) se proporciona al dependiente (consumidor) a través del constructor de clase. Otra forma sería exponer un método setEngine
en la clase Car
y usarlo para inyectar una instancia de Engine
. Esto se conoce como inyección de setter y es útil sobre todo para las dependencias que se supone que deben intercambiarse en tiempo de ejecución.
Cualquier proyecto no trivial consiste en un conjunto de componentes interdependientes y es fácil perder de vista lo que se inyecta rápidamente. Un contenedor de inyección de dependencia es un objeto que sabe cómo crear instancias y configurar otros objetos, sabe cuál es su relación con otros objetos en el proyecto y realiza la inyección de dependencia por usted. Esto le permite centralizar la administración de todas las dependencias (inter) de su proyecto y, lo que es más importante, hace posible cambiar / simular una o más de ellas sin tener que editar un montón de lugares en su código.
Dejemos a un lado la analogía del automóvil y veamos qué OP está tratando de lograr como ejemplo. Digamos que tenemos un objeto de Database
que depende del objeto mysqli
. Digamos que queremos usar una clase de contenedor de indecisión de dependencia realmente primitiva DIC
que expone dos métodos: register($name, $callback)
para registrar una forma de crear un objeto con el nombre dado y resolve($name)
para obtener el objeto de ese nombre. Nuestra configuración de contenedor se vería así:
$dic = new DIC();
$dic->register(''mysqli'', function() {
return new mysqli(''somehost'',''username'',''password'');
});
$dic->register(''database'', function() use($dic) {
return new Database($dic->resolve(''mysqli''));
});
Observe que le estamos diciendo a nuestro contenedor que tome una instancia de mysqli
de sí mismo para ensamblar una instancia de la Database
de Database
. Luego, para obtener una instancia de la Database
con su dependencia inyectada automáticamente, simplemente:
$database = $dic->resolve(''database'');
Esa es la esencia de eso. Un contenedor PHP DI / IoC algo más sofisticado pero aún relativamente simple y fácil de entender es Pimple . Verifique su documentación para más ejemplos.
Respecto al código y preguntas de OP:
- No use clases estáticas o simples para su contenedor (o para cualquier otra cosa); ambos son malvados Echa un vistazo a Pimple en su lugar.
- Decide si quieres que tu clase
mysqliWrapper
extiendamysql
o dependa de ella. - Al llamar a
IoC
desdemysqliWrapper
, está intercambiando una dependencia por otra. Sus objetos no deberían conocer ni usar el contenedor; de lo contrario, ya no es DIC, es un patrón de Localizador de Servicio (anti). - No necesita solicitar un archivo de clase antes de registrarlo en el contenedor ya que no sabe si va a utilizar un objeto de esa clase. Haga toda la configuración de su contenedor en un solo lugar. Si no usa un autocargador, puede
require
dentro de la función anónima que se registre en el contenedor.
Recursos adicionales:
- Inversión de los Contenedores de Control y el patrón de Inyección de Dependencia de Martin Fowler
- No busques cosas , un código limpio Habla sobre IoC / DI