Kaj je zabojnik DI?
Zabojnik za vbrizgavanje odvisnosti (DIC) je razred, ki lahko instancira in konfigurira objekte.
Morda vas bo presenetilo, vendar v številnih primerih ne potrebujete vsebnika za vbrizgavanje odvisnosti, da bi izkoristili prednosti vbrizgavanja odvisnosti (na kratko DI). Navsezadnje smo tudi v prejšnjem poglavju prikazali konkretne primere DI, pri čemer vsebnik ni bil potreben.
Če pa morate upravljati veliko število različnih predmetov z veliko odvisnostmi, bo vsebnik za vbrizgavanje odvisnosti resnično uporaben. To je morda primer spletnih aplikacij, zgrajenih na ogrodju.
V prejšnjem poglavju smo predstavili razreda Article
in UserController
. Oba imata nekaj odvisnosti,
in sicer podatkovno bazo in tovarno ArticleFactory
. In za ta razreda bomo zdaj ustvarili vsebnik. Seveda za tako
preprost primer ni smiselno imeti vsebnika. Vendar ga bomo ustvarili, da pokažemo, kako izgleda in deluje.
Tukaj je preprost vsebnik s trdim kodiranjem za zgornji primer:
class Container
{
public function createDatabase(): Nette\Database\Connection
{
return new Nette\Database\Connection('mysql:', 'root', '***');
}
public function createArticleFactory(): ArticleFactory
{
return new ArticleFactory($this->createDatabase());
}
public function createUserController(): UserController
{
return new UserController($this->createArticleFactory());
}
}
Uporaba bi bila videti takole:
$container = new Container;
$controller = $container->createUserController();
Za predmet samo zaprosimo vsebnik in nam ni več treba vedeti, kako ga ustvariti ali kakšne so njegove odvisnosti; vsebnik vse to ve. Odvisnosti vsebnik vnese samodejno. V tem je njegova moč.
Do zdaj je bilo v vsebniku vse zakodirano. Zato naredimo naslednji korak in dodamo parametre, da bo vsebnik resnično uporaben:
class Container
{
public function __construct(
private array $parameters,
) {
}
public function createDatabase(): Nette\Database\Connection
{
return new Nette\Database\Connection(
$this->parameters['db.dsn'],
$this->parameters['db.user'],
$this->parameters['db.password'],
);
}
// ...
}
$container = new Container([
'db.dsn' => 'mysql:',
'db.user' => 'root',
'db.password' => '***',
]);
Bolj pozorni bralci so morda opazili težavo. Vsakič, ko dobim predmet UserController
, se ustvari tudi nova
instanca ArticleFactory
in podatkovna zbirka. Tega vsekakor ne želimo.
Zato dodamo metodo getService()
, ki bo vedno znova vračala iste instance:
class Container
{
private array $services = [];
public function __construct(
private array $parameters,
) {
}
public function getService(string $name): object
{
if (!isset($this->services[$name])) {
// getService('Database') pokliče createDatabase()
$method = 'create' . $name;
$this->services[$name] = $this->$method();
}
return $this->services[$name];
}
// ...
}
Pri prvem klicu npr. na $container->getService('Database')
bo createDatabase()
ustvaril objekt
podatkovne zbirke, ki ga bo shranil v polje $services
in ga ob naslednjem klicu neposredno vrnil.
Tudi preostali del vsebnika spremenimo tako, da bo uporabljal getService()
:
class Container
{
// ...
public function createArticleFactory(): ArticleFactory
{
return new ArticleFactory($this->getService('Database'));
}
public function createUserController(): UserController
{
return new UserController($this->getService('ArticleFactory'));
}
}
Mimogrede, izraz storitev se nanaša na kateri koli predmet, ki ga upravlja vsebnik. Od tod tudi ime metode
getService()
.
Opravljeno. Imamo popolnoma funkcionalen vsebnik DI! In lahko ga uporabljamo:
$container = new Container([
'db.dsn' => 'mysql:',
'db.user' => 'root',
'db.password' => '***',
]);
$controller = $container->getService('UserController');
$database = $container->getService('Database');
Kot lahko vidite, ni težko napisati DIC. Pomembno je, da sami objekti ne vedo, da jih ustvarja vsebnik. Tako je mogoče na ta način ustvariti kateri koli objekt v PHP, ne da bi pri tem vplivali na njihovo izvorno kodo.
Ročno ustvarjanje in vzdrževanje razreda vsebnika lahko precej hitro postane nočna mora. Zato bomo v naslednjem poglavju govorili o vsebniku Nette DI, ki se lahko ustvari in posodablja skoraj samodejno.