Giriş
Modern yazılım geliştirmede bağımlılık yönetimi, kodun test edilebilirliği ve sürdürülebilirliği için çok önemlidir.
Birçok projede, nesnelerin bağımlılıklarını yönetmek için çeşitli teknikler kullanılır.
Service Locator yaklaşımı ise, ilk bakışta hayatı kolaylaştırıyor gibi görünse de, aslında ciddi sorunlara yol açan bir antipattern olarak kabul edilir.

1. Service Locator Nedir?
Service Locator, uygulama genelinde ihtiyaç duyulan bağımlılıkları merkezi bir “servis sağlayıcı” (locator) üzerinden resolve ederek (çağırarak) nesnelere erişme yaklaşımıdır.
Özetle:
-
Uygulamanın bir noktasında bir nesneye ihtiyaç olduğunda, Service Locator’dan resolve edilir.
-
Çoğu zaman bir singleton/central class ile yönetilir.

2. Service Locator’ın C# ile Temel Kullanımı
// Basit bir Service Locator örneği
public static class ServiceLocator
{
private static readonly Dictionary<Type, object> _services = new();
public static void Register<T>(T service) => _services[typeof(T)] = service;
public static T Resolve<T>() => (T)_services[typeof(T)];
}
// Kayıt:
ServiceLocator.Register<ILogger>(new FileLogger());
// Kullanım:
public class UserService
{
public void DoWork()
{
var logger = ServiceLocator.Resolve<ILogger>();
logger.Log("Çalışıyor...");
}
}
3. Service Locator Neden Antipattern?
1. Bağımlılıklar Gizlenir
-
Sınıfların gerçek bağımlılıkları constructor’da değil, Service Locator’ın içinde “saklanır”.
-
Kod okuyan veya test eden birisi, nesnenin hangi servislere bağımlı olduğunu doğrudan göremez.
2. Test Edilebilirlik Azalır
-
Mock/Stub kullanmak zorlaşır. Testlerde “hangi servisi nereden, nasıl vereceğini” takip etmek zorlaşır.
-
Dependency Injection ile karşılaştırıldığında, kodun izolasyonu bozulur.
3. Global Durum ve Yan Etki
-
Service Locator global bir nesneye dönüşür, singleton gibi uygulamanın her yerine yayılır.
-
Global state yönetimiyle ilgili klasik sorunlar ortaya çıkar.
4. Gizli Bağımlılıklar ve Sıkı Bağlılık
-
Kodun bağımlılıkları dışarıdan bakınca belli olmaz, kodun bakımı ve refactoring’i zorlaşır.
-
Refactoring sırasında yanlışlıkla bağımlılıklar gözden kaçabilir.
4. Hangi Sorunlara Yol Açar?
-
Kodun anlaşılabilirliğini ve okunabilirliğini azaltır.
-
Test yazmayı ve otomasyonunu zorlaştırır.
-
Refactoring’de hata yapma olasılığını artırır.
-
Bağımlılıkları değiştirmek daha çok “runtime error” riskine neden olur.
-
Kodun farklı yerlerinde gizli yan etkiler oluşur.
5. Dependency Injection ile Karşılaştırma
Dependency Injection (DI) Nedir?
Bağımlılıkların, ihtiyaç duyan nesneye constructor veya method ile dışarıdan verilmesidir.
Bağımlılıklar açıkça tanımlanır, kolayca test edilir, yönetilir.
Karşılaştırma Tablosu
| Özellik | Service Locator | Dependency Injection |
|---|---|---|
| Bağımlılıklar | Gizli | Açık (constructor’da) |
| Test Edilebilirlik | Düşük | Yüksek |
| Refactoring Kolaylığı | Zor | Kolay |
| Kod Okunabilirliği | Düşük | Yüksek |
| Yan Etki | Fazla | Az |
| Singleton/Global Riski | Yüksek | Düşük |
DI ile Doğru Bağımlılık Yönetimi Örneği (C#)
public class UserService
{
private readonly ILogger _logger;
public UserService(ILogger logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.Log("Çalışıyor...");
}
}
// Kullanımda:
var userService = new UserService(new FileLogger());
6. Service Locator’ın Alternatifleri
-
Constructor Injection
-
Bağımlılık doğrudan constructor’da tanımlanır.
-
-
Method Injection
-
Sadece ilgili methodda dependency geçilir.
-
-
Property Injection
-
İhtiyaca göre dependency, property olarak set edilir.
-
-
IoC Container Kullanımı
-
.NET Core built-in DI, Autofac, Ninject gibi araçlarla yönetilen, tür güvenli ve merkezi DI sistemleri.
-
7. C# ile Doğru Bağımlılık Yönetimi (Best Practice)
public interface IUserRepository
{
void Save(User user);
}
public class UserService
{
private readonly IUserRepository _repository;
private readonly ILogger _logger;
public UserService(IUserRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public void RegisterUser(User user)
{
_repository.Save(user);
_logger.Log("Yeni kullanıcı kaydı yapıldı.");
}
}
// Kayıt ve kullanım:
var userService = new UserService(new UserRepository(), new FileLogger());
Burada UserService içinde hangi bağımlılıklar var, doğrudan constructor’dan okunabiliyor ve kolayca mock’lanabiliyor.
8. Sıkça Sorulan Sorular
Service Locator her zaman kötü mü?
Küçük, deneme veya test amaçlı projelerde hayatı kolaylaştırabilir. Ancak ölçeklenen, uzun süre yaşayacak projelerde kesinlikle kaçınılmalıdır.
IoC Container kullandığımda Service Locator’a ihtiyacım var mı?
Hayır, IoC Container ile dependency’leri doğrudan injection yoluyla yönetmek daha doğrudur.
Legacy kodda Service Locator varsa ne yapmalı?
Yavaş yavaş refactoring ile bağımlılıkları constructor’a taşıyıp, testleri iyileştirerek DI’a geçiş yapabilirsin.
9. Sonuç ve Pratik İpuçları
-
Bağımlılıkları gizleme, açıkça yönet!
-
Kodun test edilebilirliğini artırmak için DI kullan.
-
Service Locator yerine .NET Core’un built-in DI veya üçüncü parti container kullan.
-
Refactoring fırsatı bulduğunda, Service Locator’dan uzaklaş.
-
Kodun bağımlılıkları her zaman “gözle görülür” ve “mock’lanabilir” olmalı.