Avance rapide:
J'écris ceci dans le but d'avoir une meilleure compréhension de l'injection de dépendances et des conteneurs IoC, mais aussi afin que je puisse ensuite corriger les erreurs et l'utiliser pour aider à en apprendre quelques-uns à mes amis.
À partir de maintenant, j'ai essayé de lire la documentation de divers cadres (laravel, fuel, codeigniter, symfony) et j'ai trouvé qu'il y avait trop d'aspects différents des cadres dont j'avais besoin pour me sentir à l'aise de l'utiliser que j'ai décidé d'essayer de apprenez individuellement chacune des pièces majeures individuellement avant d'essayer de les utiliser dans les cadres eux-mêmes.
J'ai passé des heures à googler différentes significations, à regarder les réponses de stackoverflow et à lire divers articles essayant de comprendre ce qu'est un IoC et comment l'utiliser pour gérer correctement les dépendances, et je crois que je comprends de quoi il s'agit, mais je suis toujours gris sur la façon de le mettre en œuvre correctement. Je pense que la meilleure façon pour quiconque lit ceci de m'aider est de donner ma compréhension actuelle des conteneurs IoC et de l'injection de dépendance, puis de laisser les gens qui ont une meilleure compréhension que moi indiquer où ma compréhension est insuffisante.
Ma compréhension:
Donc, à ce stade, je commence à essayer d'utiliser le conteneur IoC pour des scénarios plus compliqués. À partir de maintenant, il semble que pour utiliser le conteneur IoC, je suis limité à une relation has-a pour à peu près n'importe quelle classe que je veux créer qui a des dépendances qu'il veut définir dans le conteneur IoC. Que faire si je veux créer une classe qui hérite d'une classe, mais uniquement si la classe parente a été créée d'une manière spécifique, elle a été enregistrée dans le conteneur IoC.
Ainsi, par exemple: je veux créer une classe enfant de mysqli, mais je veux enregistrer cette classe dans le conteneur IoC pour instancier uniquement avec la classe parent construite d'une manière que j'ai précédemment enregistrée dans le conteneur IoC. Je ne peux pas penser à un moyen de le faire sans dupliquer le code (et comme il s'agit d'un projet d'apprentissage, j'essaie de le garder aussi pur que possible). Voici quelques autres exemples de ce que j'essaie de décrire.
Voici donc certaines de mes questions:
Je sais que c'est extrêmement long, et je voulais juste remercier d'avance tous ceux qui ont pris le temps de le lire, et plus encore ceux qui partagent leurs connaissances.
En termes simples (car ce n'est pas un problème limité à OOP monde uniquement), une dépendance est une situation où le composant A a besoin (dépend) du composant B pour faire ce qu'il est censé faire. Le mot est également utilisé pour décrire le composant dépendant dans ce scénario. Pour mettre cela en termes OOP/PHP, considérez l'exemple suivant avec l'analogie obligatoire de la voiture :
class Car {
public function start() {
$engine = new Engine();
$engine->vroom();
}
}
Car
dépend de Engine
. Engine
est la dépendance de Car
. Ce morceau de code est plutôt mauvais, car:
Car
Engine
par MockEngine
à des fins de test ou TurboEngine
qui étend l'original sans modifier le Car
. L'injection de dépendances est un moyen de résoudre tous ces problèmes en rendant explicite et explicite le fait que Car
a besoin de Engine
avec un:
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);
Ce qui précède est un exemple d'injection de constructeur , dans lequel la dépendance (l'objet dépendant) est fournie au dépendant (consommateur) via le constructeur de classe . Une autre façon serait d'exposer une méthode setEngine
dans la classe Car
et de l'utiliser pour injecter une instance de Engine
. Ceci est connu comme injection de setter et est surtout utile pour les dépendances qui sont censées être échangées au moment de l'exécution.
Tout projet non trivial se compose d'un tas de composants interdépendants et il devient facile de perdre la trace de ce qui est injecté où assez rapidement. Un conteneur d'injection de dépendances est un objet qui sait comment instancier et configurer d'autres objets, sait quelle est leur relation avec les autres objets du projet et fait la dépendance injection pour vous. Cela vous permet de centraliser la gestion de toutes les (inter) dépendances de votre projet et, plus important encore, permet de modifier/simuler un ou plusieurs d'entre eux sans avoir à modifier un tas d'endroits dans votre code.
Abandonnons l'analogie avec la voiture et examinons ce que OP essaie de réaliser à titre d'exemple. Disons que nous avons un objet Database
en fonction de l'objet mysqli
. Disons que nous voulons utiliser une classe de conteneur d'indection de dépendance vraiment primitive DIC
qui expose deux méthodes: register($name, $callback)
pour enregistrer une manière de créer un objet sous le nom donné et resolve($name)
pour obtenir l'objet de ce nom. Notre configuration de conteneur ressemblerait à ceci:
$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'));
});
Notez que nous demandons à notre conteneur de récupérer une instance de mysqli
à partir de lui-même pour assembler une instance de Database
. Ensuite, pour obtenir une instance de Database
avec sa dépendance automatiquement injectée, nous devrions simplement:
$database = $dic->resolve('database');
C'est l'essentiel. Un conteneur un peu plus sophistiqué mais toujours relativement simple et facile à saisir PHP DI/IoC est Pimple . Consultez sa documentation pour plus d'exemples.
Concernant le code OP et les questions:
mysqliWrapper
étende mysql
ou en dépend .IoC
depuis mysqliWrapper
, vous échangez une dépendance pour une autre. Vos objets ne doivent pas être conscients ni utiliser le conteneur; sinon ce n'est plus DIC, c'est le modèle (Anti) Service Locator.require
un fichier de classe avant de l'enregistrer dans le conteneur car vous ne savez pas du tout si vous allez utiliser un objet de cette classe. Faites toute votre installation de conteneur en un seul endroit. Si vous n'utilisez pas de chargeur automatique, vous pouvez require
à l'intérieur de la fonction anonyme que vous enregistrez avec le conteneur.Ressources supplémentaires: