Dependency Injection nedir
Dependency Injection (DI), yazılım tasarımında kullanılan bir desendir ve bir sınıfın bağımlı olduğu diğer sınıfları (dependencies) dışarıdan alarak, bu bağımlılıkların sınıf içinde doğrudan oluşturulmasını engellemeyi amaçlar. Bu sayede kod daha esnek, test edilebilir ve bakımı kolay hale gelir.
Dependency Injection'in Temel Amacı: Bağımlılıkların Yönetimi: Bir sınıfın ihtiyaç duyduğu diğer sınıflar (servisler, repositoryler, vs.) dışarıdan verilir. Bu, sınıfın bu bağımlılıkları kendi içinde oluşturmasını engeller.
Esneklik: Bağımlılıklar dışarıdan enjekte edildiği için, farklı implementasyonlar kolayca değiştirilebilir.
Test Edilebilirlik: Bağımlılıklar mock veya stub nesnelerle değiştirilebilir, bu da unit testlerin daha kolay yazılmasını sağlar.
Sorumlulukların Ayrılması: Sınıflar, bağımlılıklarını nasıl elde edeceği konusunda endişelenmez, sadece iş mantığına odaklanır.
Dependency Injection Türleri: Constructor Injection:
Bağımlılıklar, sınıfın constructor'ı (yapıcı metodu) aracılığıyla enjekte edilir.
En yaygın ve tercih edilen yöntemdir.
Örnek:
public class UserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void AddUser(User user)
{
_userRepository.Add(user);
}
}
Property Injection:
Bağımlılıklar, sınıfın public property'leri aracılığıyla enjekte edilir.
Daha az tercih edilir, çünkü bağımlılıkların zorunlu olup olmadığı net değildir.
Örnek:
public class UserService
{
public IUserRepository UserRepository { get; set; }
public void AddUser(User user)
{
UserRepository.Add(user);
}
}
Method Injection:
Bağımlılıklar, bir metot aracılığıyla enjekte edilir.
Genellikle sadece belirli bir metot için bağımlılık gerektiğinde kullanılır.
Örnek:
public class UserService
{
public void AddUser(User user, IUserRepository userRepository)
{
userRepository.Add(user);
}
}
Dependency Injection Container (DI Container) DI Container'lar, bağımlılıkların otomatik olarak yönetilmesini sağlayan araçlardır. Örneğin, .NET'te Microsoft.Extensions.DependencyInjection, Java'da Spring Framework, veya Node.js'de Inversify gibi kütüphaneler kullanılır.
Kayıt (Register): Hangi interface'in hangi sınıfla implemente edileceği belirlenir.
Çözümleme (Resolve): Container, bağımlılıkları otomatik olarak çözümler ve sınıflara enjekte eder.
Örnek (C#):
var services = new ServiceCollection();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<UserService>();
var serviceProvider = services.BuildServiceProvider();
var userService = serviceProvider.GetService<UserService>();
Avantajları: Kodun Daha Az Bağımlı Olması: Sınıflar, somut implementasyonlara değil, soyutlamalara (interface'lere) bağlıdır.
Test Edilebilirlik: Mock nesnelerle kolayca test yapılabilir.
Yeniden Kullanılabilirlik: Bağımlılıklar farklı projelerde veya modüllerde kullanılabilir.
Bakım Kolaylığı: Bağımlılıklar merkezi bir şekilde yönetilir.
.NET Dependency Injection (DI) içinde Transient, Scoped ve Singleton yaşam döngüleri (lifetime) önemlidir çünkü bağımlılıkların uygulama içinde nasıl yönetileceğini belirler. Transient ve Singleton özellikle performans, bellek kullanımı ve thread safety açısından kritik seçimlerdir.
Transient, Singleton, ve Scoped, Dependency Injection (DI) Container'larında kullanılan servis ömür türleridir (service lifetimes). Bu kavramlar, bir servisin ne zaman ve nasıl oluşturulacağını, ne zaman yok edileceğini ve ne kadar süreyle yaşayacağını belirler. Her bir ömür türü, farklı senaryolara uygun olacak şekilde tasarlanmıştır.
1. Transient
Tanım: Her seferinde yeni bir örnek (instance) oluşturulur.
Kullanım Alanı: Her çağrıldığında yeni bir nesne gerektiren durumlarda kullanılır.
Özellikler:
Her bağımlılık çözümlendiğinde yeni bir örnek oluşturulur.
Performans açısından maliyetli olabilir, çünkü sık sık nesne oluşturulur.
Genellikle hafif ve durumsuz (stateless) servisler için tercih edilir.
2. Singleton
Tanım: Uygulama boyunca tek bir örnek kullanılır.
Kullanım Alanı: Uygulama genelinde paylaşılan ve durum bilgisi tutan servisler için idealdir.
Özellikler:
İlk çağrıldığında bir örnek oluşturulur ve bu örnek uygulama boyunca kullanılır.
Performans açısından avantajlıdır, çünkü sadece bir kez nesne oluşturulur.
Thread-safe olmasına dikkat edilmelidir, çünkü aynı örnek birden fazla thread tarafından kullanılabilir.
3. Scoped
Tanım: Her bir istek (request) veya kapsam (scope) için bir örnek oluşturulur.
Kullanım Alanı: Web uygulamalarında her HTTP isteği için bir örnek oluşturmak için kullanılır.
Özellikler:
Aynı kapsam (scope) içinde aynı örnek kullanılır.
Farklı kapsamlarda farklı örnekler oluşturulur.
Genellikle durum bilgisi tutan ve bir istek boyunca kullanılması gereken servisler için tercih edilir.