Yazılım geliştirmenin temel hedeflerinden biri, kodun açık, bakımı kolay ve esnek olmasıdır. Bu hedeflere ulaşmanın bir yolu da “Bağımlılık Enjeksiyonu” (Dependency Injection – DI) tasarım ilkesini uygulamaktır. Bağımlılık Enjeksiyonuna aşağıdaki yazımızda daha önce değinmiştik.
Şimdi konuyu bir adım öteye taşıyarak: Bu yazıda, bağımlılık enjeksiyonunun ne olduğunu, neden önemli olduğunu ve nasıl kullanıldığını anlatırken, basit bir şekilde kendi enjesiyon modelimizi geliştireceğiz. Öncelikle Bağımlılık Enjeksiyonu konusunu yeniden hatırlayalım.
Bağımlılık Enjeksiyonu Nedir?
Bağımlılık enjeksiyonu, bir nesnenin bağımlılıklarını (diğer nesneleri veya değerleri) dışarıdan almasını sağlayan bir tasarım desenidir. Bu, nesnelerin kendi bağımlılıklarını yönetmemesi, bunun yerine dışarıdan alması anlamına gelir. Bu yöntem, kodun esnek ve test edilebilir olmasını sağlar.
Neden Bağımlılık Enjeksiyonu Kullanmalıyız?
Esneklik ve Değiştirilebilirlik: Bağımlılık enjeksiyonu, kodunuzun daha az bağımlılığa sahip olmasını sağlar. Bu da, kodunuzu daha esnek ve değiştirilebilir kılar. Bir bağımlılığı değiştirmek veya güncellemek istediğinizde, sadece bağımlılığı enjekte ettiğiniz yerleri güncellemeniz yeterlidir.
Test Edilebilirlik: Bağımlılık enjeksiyonu, kodunuzu birim test etmeyi kolaylaştırır. Bir nesnenin bağımlılıkları dışarıdan alındığı için, bu bağımlılıkları taklit eden sahte (mock) nesneleri enjekte ederek daha iyi testler yazabilirsiniz.
Okunabilirlik ve Bakım Kolaylığı: Bağımlılık enjeksiyonu, kodunuzu daha okunabilir ve bakımı daha kolay hale getirir. Bir nesnenin bağımlılıklarını doğrudan içinde oluşturmak yerine, bu bağımlılıkları constructor veya setter metodları aracılığıyla enjekte etmek, kodun anlaşılabilirliğini artırır.
Bağımlılık Enjeksiyonu Türleri
Constructor Enjeksiyonu: Bağımlılıklar, nesnenin constructor metoduna parametre olarak geçirilir.
class MyClass {
private $dependency;
public function __construct(Dependency $dependency) {
$this->dependency = $dependency;
}
}
Setter Metodu İle Enjeksiyon: Bağımlılıklar, setter metodları kullanılarak enjekte edilir.
class MyClass {
private $dependency;
public function setDependency(Dependency $dependency) {
$this->dependency = $dependency;
}
}
Metot Enjeksiyonu: Bağımlılıklar, sadece belirli metotlara parametre olarak geçirilir.
class MyClass {
public function myMethod(Dependency $dependency) {
// Metot içinde bağımlılık kullanımı
}
}
Bağımlılık Enjeksiyonu Örneği
Aşağıda, PHP dilinde bağımlılık enjeksiyonu kullanılarak bir örnek sınıf yer almaktadır:
class UserService {
private $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
public function getUserById($userId) {
return $this->userRepository->findById($userId);
}
}
Yukarıdaki örnekte, UserService
sınıfı UserRepository
bağımlılığını constructor metodu aracılığıyla alır.
Bağımlılık enjeksiyonu, yazılım geliştirme sürecinde kodun daha esnek, test edilebilir ve bakımı kolay olmasını sağlayan önemli bir tasarım ilkesidir. Uygulamak, yazılım projelerinizde daha temiz ve sürdürülebilir bir kod elde etmenize yardımcı olabilir.
Bu tasarım ilkesini doğru bir şekilde anlamak ve kullanmak, geliştiricilerin kodlarını daha iyi organize etmelerine ve gelecekteki değişikliklere daha iyi uyum sağlamalarına yardımcı olabilir.
Kendi Bağımlılık Enjeksiyonu Sistemimizi Tasarlayalım
Bağımlılık enjeksiyonu ilkesi hakkında yeterince bilgiye sahip olduğumuza göre, artık kenti Enjeksiyon mekanizmamızı geliştirebiliriz. Buradaki örnekte Bağımlılık Enjeksiyonunu (Dependency Injection) nasıl projemizde kullanabileceğimize değineceğiz. Yazacağımız kodlar örnek mahiyetindedir, gerçek bir projede kullanmak mümkün olsa da geliştirmeler gerekecektir.
İhtiyaçlarımızı belirleyelim
Öncelikli olarak bağımlılıklarımızı belirlememiz gerekiyor. Projemizde ihtiyaç duyacağımız gevşek bağ gerektiren bağımlılıklar nelerdir bunlara karar verelim.
- Genel olarka her projede istisnalar olsa da bir veritabanı ihtiyacı var. Bu nedenle bir database bağlantısı ekleyelim,
- Projemiz kullanıcıdan istekler alacak ve cevaplar verecek bunun içinde request ve response şeklinde iki bağımız daha olsun,
- Projemiz bir görüntüleme arabirimine ihtiyaç duyacaktır bu nedenle birde view ekleyelim,
Bu örnekleri session, cookie gibi çoğaltabiliriz. Ancak şimdilik örneğimiz için bunlar bize yeterli olacaktır.
Dependency Injection Sınıfımızı İnşaa Edelim
İhtiyaçlarımızı belirlediğimize göre artık sınıfımızı inşaa etmeye başlayabiliriz. Öncelikli karar vermemiz gereken ne tür bir injeksiyon gerçekleştireceğimiz. Biz örneğimizde Constructor yöntemini benimseyeceğiz. Di sınıfımız aşağıdaki gibi başlayacak
class Di {
//Bağımlılıklarımızı burada store edeceğiz
private $dependencies = [];
//Bağımlılığı set etmek için kullanılan metod
public function set($name, $value)
{
$this->dependencies[$name] = $value;
}
// Bağımlılığı almak için kullanılan metod
public function get()
{
//...
}
}
Sınıfımızda temel olarka “set” ve “get” olmak üzere iki metodumuz var. set metodu ile bağımlılıklarımızı tanımlayacağız. get metodu ile de bağımlılıkları ihtiyaç duyduğumuzda çağıracağız. Get metodumuzun bize biraz kolaylık sağlamasını ve maliyetleri minimuma indirmesini istiyoruz. Çalışma zamanı sırasında tüm bağımlılıklar tanımlı olsa da kullanılmayabilir. Kullanımayan bağımlılıkların hiç çalıştırılmamasını ve ihtiyaç anında yüklenmesini isteyebiliriz. Bu nedenle get metodumuzu sadece ihtiyaç duyduğumuzda bağımlılığı çalıştıracak şekilde dizayn edeceğiz. Kodumuz aşağıdaki gibi olacak;
class Di {
//Bağımlılıklarımızı burada store edeceğiz
private $dependencies = [];
//Bağımlılığı set etmek için kullanılan metod
public function set($name, $value)
{
$this->dependencies[$name] = $value;
}
// Bağımlılığı almak için kullanılan metod
public function get($name)
{
if(!isset($this->dependencies[$name])){
//eğer istek yapılan bağımlılık set edilmemiş ise burada varsayılan bir bağımlılık sınıfı enjekte edebiliriz.
// Bu örnekte varsayılan boş sınıf stdClass olacak
$this->set($name, new \stdClass());
}
$dependency = $this->dependencies[$name];
// Eğer bağımlılık bir fonksiyon ise fonksiyonu çağıralım
if(is_callable($dependency))
{
// Bağımlılığın devamı için fonksyion içerisine enjesiyon taşıyıcımızı geçiriyoruz.
$dependency = $dependency($this);
}
// Eğer bağımlılık bir sınıf ismi ise örnekliyoruz
if(is_string($dependency) && class_exists($dependency))
{
// Bağımlılığın devamı için oluşturucuya enjesiyon taşıyıcımızı geçiriyoruz.
$dependency = new $dependency($this);
}
// Eğer bağımlılığımız ilk defa çağırılıyorsa veya oluşturulmamış bir örnek ise burada oluşturduğumuz örneği set ediyoruz.
if(!is_object($this->dependencies[$name])){
$this->set($name, $dependency);
}
return $dependency;
}
}
Şimdi gelelim işin içine biraz sihir eklemeye. Bağımlılıklarımızı sihirli metodlarla daha kolay çağıralım.
// Sihirli çağırma metodumuz
public function __call($method, $args)
{
if(isset($this->dependencies[$method])){
return $this->get($method);
}
throw new \BadMethodCallException("Undefined method '$method'");
}
Sınıfımızın son hali aşağıdaki gibi olacak.
class Di {
//Bağımlılıklarımızı burada store edeceğiz
private $dependencies = [];
//Bağımlılığı set etmek için kullanılan metod
public function set($name, $value)
{
$this->dependencies[$name] = $value;
}
// Bağımlılığı almak için kullanılan metod
public function get($name)
{
if(!isset($this->dependencies[$name])){
//eğer istek yapılan bağımlılık set edilmemiş ise burada varsayılan bir bağımlılık sınıfı enjekte edebiliriz.
// Bu örnekte varsayılan boş sınıf stdClass olacak
$this->set($name, new \stdClass());
}
$dependency = $this->dependencies[$name];
// Eğer bağımlılık bir fonksiyon ise fonksiyonu çağıralım
if(is_callable($dependency))
{
// Bağımlılığın devamı için fonksyion içerisine enjesiyon taşıyıcımızı geçiriyoruz.
$dependency = $dependency($this);
}
// Eğer bağımlılık bir sınıf ismi ise örnekliyoruz
if(is_string($dependency) && class_exists($dependency))
{
// Bağımlılığın devamı için oluşturucuya enjesiyon taşıyıcımızı geçiriyoruz.
$dependency = new $dependency($this);
}
// Eğer bağımlılığımız ilk defa çağırılıyorsa veya oluşturulmamış bir örnek ise burada oluşturduğumuz örneği set ediyoruz.
if(!is_object($this->dependencies[$name])){
$this->set($name, $dependency);
}
return $dependency;
}
// Sihirli çağırma metodumuz
public function __call($method, $args)
{
if(isset($this->dependencies[$method])){
return $this->get($method);
}
throw new \BadMethodCallException("Undefined method '$method'");
}
}
Haydi şimdi Di sınıfımızı kullanarak bir kaç örnek gerçekleştirelim.
// Di sınıfını örnekleyelim
$di = new Di();
// db bağımlılığını set edelim
$di->set('db', function ($di) {
// Burada varsayılan bir DB sınıfı örneklenmiştir, gerçek bir bağlantı kurulmamıştır
return new DB();
});
// request bağımlılığını set edelim
$di->set('request', 'RequestHandler');
// view bağımlılığını set edelim
$di->set('view', function ($di) {
// Burada varsayılan bir View sınıfı örneklenmiştir, gerçek bir şablon kullanılmamıştır
return new View($di);
});
// db bağımlılığını alalım ve bir sorgu yapalım
$db = $di->get('db');
$result = $db->fetch("SELECT * FROM table");
// Sihirli metodu kullanarak db bağımlılığını alalım
$anotherDb = $di->db;
// request bağımlılığını alalım ve bir post değeri alalım
$request = $di->get('request');
$postValue = $request->getPost('variable', 'filter', 'default');
// view bağımlılığını alalım ve bir değişken set edelim
$view = $di->get('view');
$view->setVar('key', 'value');
// Sonuçları ekrana yazdıralım
echo "DB Result: $result<br>";
echo "Request Post Value: $postValue<br>";
echo "View Var Set: Key = 'value'";
// Sihirli metodu kullanarak view bağımlılığını alalım
$anotherView = $di->view;
Bağımlılık enjeksiyonu, kodunuzu modüler hale getirmenin ve değişikliklere daha uyumlu hale getirmenin güçlü bir yoludur. Kendi Di
sınıfınızı oluşturarak, bağımlılıkları yönetmek ve her bir modülü birbirine bağlamak için basit ve etkili bir yöntem elde ettiniz. Bu sayede, kodunuzu daha iyi organize edebilir, birim testleri daha kolay yazabilir ve gelecekteki değişikliklere daha hazırlıklı olabilirsiniz.
Bağımlılık enjeksiyonunu kullanarak yazılım geliştirmeye devam ederek, kodunuzun esnek ve sürdürülebilir olmasını sağlayabilir ve daha kaliteli projelere imza atabilirsiniz.
“Bağımlılık Enjeksiyonu: Kodunuzda Esnekliği Sağlayan Güçlü Bir Tasarım İlkesi” için bir yanıt
[…] ileri tekniklerde bir Dependency Injection yani bağımlığık enjeksiyonu kullanmak daha doğru bir […]