DI Konteyner Nedir?
Bağımlılık enjeksiyonu konteyneri (DIC), nesneleri örnekleyebilen ve yapılandırabilen bir sınıftır.
Belki sizi şaşırtacak ama birçok durumda bağımlılık enjeksiyonunun (kısaca DI) avantajlarından yararlanmak için bir bağımlılık enjeksiyonu konteynerine ihtiyacınız yoktur. Sonuçta, giriş bölümü içinde bile DI'yi somut örneklerle gösterdik ve hiçbir konteynere gerek yoktu.
Ancak, birçok bağımlılığa sahip çok sayıda farklı nesneyi yönetmeniz gerekiyorsa, bir bağımlılık enjeksiyonu konteyneri gerçekten faydalı olacaktır. Bu, örneğin bir framework üzerine kurulu web uygulamaları için geçerlidir.
Önceki bölümde Article
ve UserController
sınıflarını tanıttık. Her ikisinin de bazı
bağımlılıkları var, yani veritabanı ve ArticleFactory
fabrikası. Ve şimdi bu sınıflar için bir konteyner
oluşturacağız. Elbette, böylesine basit bir örnek için bir konteynere sahip olmanın anlamı yok. Ama nasıl
göründüğünü ve çalıştığını göstermek için onu oluşturacağız.
İşte belirtilen örnek için basit, sabit kodlanmış (hardcoded) bir konteyner:
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());
}
}
Kullanım şöyle görünürdü:
$container = new Container;
$controller = $container->createUserController();
Konteynere sadece nesneyi sorarız ve artık onu nasıl oluşturacağımızı ve bağımlılıklarının ne olduğunu bilmemize gerek yoktur; tüm bunları konteyner bilir. Bağımlılıklar konteyner tarafından otomatik olarak enjekte edilir. Gücü buradadır.
Konteyner şimdilik tüm verileri sabit (hardcoded) olarak yazmıştır. Bu yüzden bir sonraki adımı atacağız ve konteynerin gerçekten kullanışlı olması için parametreler ekleyeceğiz:
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' => '***',
]);
Dikkatli okuyucular belki de belirli bir sorunu fark etmişlerdir. UserController
nesnesini her aldığımda, yeni
bir ArticleFactory
örneği ve veritabanı da oluşturulur. Bunu kesinlikle istemiyoruz.
Bu yüzden, her zaman aynı örnekleri döndürecek olan getService()
metodunu ekleyeceğiz:
class Container
{
private array $services = [];
public function __construct(
private array $parameters,
) {
}
public function getService(string $name): object
{
if (!isset($this->services[$name])) {
// getService('Database') createDatabase() çağıracak
$method = 'create' . $name;
$this->services[$name] = $this->$method();
}
return $this->services[$name];
}
// ...
}
Örneğin $container->getService('Database')
ilk çağrıldığında, createDatabase()
'den
veritabanı nesnesini oluşturmasını ister, onu $services
dizisine kaydeder ve bir sonraki çağrıda doğrudan onu
döndürür.
Konteynerin geri kalanını da getService()
kullanacak şekilde düzenleyeceğiz:
class Container
{
// ...
public function createArticleFactory(): ArticleFactory
{
return new ArticleFactory($this->getService('Database'));
}
public function createUserController(): UserController
{
return new UserController($this->getService('ArticleFactory'));
}
}
Bu arada, servis terimi konteyner tarafından yönetilen herhangi bir nesneyi ifade eder. Bu yüzden metot adı
getService()
'dir.
Bitti. Tamamen işlevsel bir DI konteynerimiz var! Ve onu kullanabiliriz:
$container = new Container([
'db.dsn' => 'mysql:',
'db.user' => 'root',
'db.password' => '***',
]);
$controller = $container->getService('UserController');
$database = $container->getService('Database');
Gördüğünüz gibi, bir DIC yazmak karmaşık bir şey değil. Nesnelerin kendilerinin bir konteyner tarafından oluşturulduğunu bilmediklerini hatırlatmakta fayda var. Bu nedenle, kaynak koduna müdahale etmeden PHP'deki herhangi bir nesneyi bu şekilde oluşturmak mümkündür.
Konteyner sınıfını manuel olarak oluşturmak ve bakımını yapmak oldukça hızlı bir şekilde bir kabusa dönüşebilir. Bu yüzden bir sonraki bölümde, neredeyse kendi kendine üretebilen ve güncelleyebilen Nette DI Konteyner hakkında konuşacağız.