Bağımlılık ekleme (DI), programlamada yaygın olarak kullanılan ve Android geliştirme için çok uygun olan bir tekniktir. DI ilkelerine uyarak iyi uygulama mimarisinin temelini hazırlarsınız.
Bağımlılık yerleştirmeyi uygulamak size aşağıdaki avantajları sağlar:
- Kodun yeniden kullanılabilirliği
- Yeniden düzenleme kolaylığı
- Test kolaylığı
Bağımlılık yerleştirme ile ilgili temel bilgiler
Özellikle Android'de bağımlılık yerleştirmeye değinmeden önce, bu sayfada bağımlılık eklemenin işleyiş şekliyle ilgili daha genel bir genel bakış sunulmaktadır.
Bağımlılık yerleştirme nedir?
Sınıflar genellikle diğer sınıflara referans gerektirir. Örneğin, bir Car
sınıfının Engine
sınıfına referans vermesi gerekebilir. Bu gerekli sınıflara bağımlılıklar adı verilir. Bu örnekte Car
sınıfı, çalışacak Engine
sınıfının bir örneğine sahip olmaya bağlıdır.
Bir sınıfın ihtiyaç duyduğu bir nesneyi alabilmesinin üç yolu vardır:
- Sınıf, ihtiyaç duyduğu bağımlılığı oluşturur. Yukarıdaki örnekte
Car
, kendiEngine
örneğini oluşturup başlatır. - Başka bir yerden almak.
Context
alıcıları vegetSystemService()
gibi bazı Android API'leri bu şekilde çalışır. - Parametre olarak sağlayın. Uygulama, sınıf oluşturulduğunda bu bağımlılıkları sağlayabilir veya her bir bağımlılığa ihtiyacı olan işlevlere aktarabilir. Yukarıdaki örnekte,
Car
oluşturucusu parametre olarakEngine
alır.
Üçüncü seçenek ise bağımlılık yerleştirmedir. Bu yaklaşımda, sınıfın bağımlılıklarını alır ve sınıf örneğinin bunları elde etmesini sağlamak yerine bunları sağlarsınız.
Bir örnekle açıklayalım. Bağımlılık yerleştirme kullanılmadığında, kodda kendi Engine
bağımlılığını oluşturan Car
öğesini temsil etmek aşağıdaki gibi görünür:
Kotlin
class Car { private val engine = Engine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }
Java
class Car { private Engine engine = new Engine(); public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }

Car
sınıfı kendi Engine
alanını oluşturduğundan bu, bağımlılık yerleştirmeye bir örnek değildir. Bu, aşağıdaki nedenlerle sorunlu olabilir:
Car
veEngine
birbiriyle sıkı sıkıya bağlıdır.Car
örneğinin birEngine
türü kullanır ve hiçbir alt sınıf ya da alternatif uygulama kolayca kullanılamaz.Car
kendiEngine
değerini oluşturursa yalnızcaGas
veElectric
türündeki motorlar için aynıCar
öğesini yeniden kullanmak yerine iki türCar
oluşturmanız gerekir.Engine
platformuna duyulan aşırı bağımlılık, testi daha zor hale getirir.Car
, gerçek birEngine
örneği kullandığından farklı test durumları içinEngine
öğesini değiştirmek üzere test ikilisi kullanmanızı engeller.
Bağımlılık yerleştirme ile kod nasıl görünür? Başlatma sırasında kendi Engine
nesnesini oluşturan her Car
örneği yerine, oluşturucuda parametre olarak bir Engine
nesnesi alır:
Kotlin
class Car(private val engine: Engine) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() }
Java
class Car { private final Engine engine; public Car(Engine engine) { this.engine = engine; } public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Engine engine = new Engine(); Car car = new Car(engine); car.start(); } }

main
işlevi Car
kullanır. Car
, Engine
kaynağına bağlı olduğundan uygulama Engine
örneği oluşturur ve daha sonra bu örneği Car
örneği oluşturmak için kullanır. DI tabanlı bu yaklaşımın avantajları:
Car
yeniden kullanılabilirliği.Car
için farklıEngine
uygulamalarını aktarabilirsiniz. Örneğin,Car
tarafından kullanılmasını istediğinizElectricEngine
adında yeni birEngine
alt sınıfı tanımlayabilirsiniz. DI kullanırsanız tek yapmanız gereken, güncellenmişElectricEngine
alt sınıfının bir örneğine geçmektir.Car
yine de herhangi bir değişiklik yapmadan çalışır.Car
kolayca test edilebilir. Farklı senaryolarınızı test etmek için iki kez test edebilirsiniz. Örneğin,Engine
içinFakeEngine
adlı bir test ikilisi oluşturabilir ve bunu farklı testler için yapılandırabilirsiniz.
Android'de bağımlılık eklemenin iki temel yolu vardır:
Oluşturucu Yerleştirme. Bu, yukarıda açıklanan şekildedir. Bir sınıfın bağımlılıklarını kurucuya iletirsiniz.
Alan Yerleştirme (veya Setter Yerleştirme). Etkinlikler ve parçalar gibi belirli Android çerçeve sınıfları sistem tarafından örneklenir. Bu nedenle, kurucu yerleştirme mümkün değildir. Alan yerleştirme yönteminde bağımlılıklar sınıf oluşturulduktan sonra somutlaştırılır. Kod şöyle görünür:
Kotlin
class Car { lateinit var engine: Engine fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.engine = Engine() car.start() }
Java
class Car { private Engine engine; public void setEngine(Engine engine) { this.engine = engine; } public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.setEngine(new Engine()); car.start(); } }
Otomatik bağımlılık yerleştirme
Önceki örnekte, bir kitaplığa ihtiyaç duymadan farklı sınıfların bağımlılıklarını kendiniz oluşturdunuz, sağladınız ve yönettiniz. Buna el ile bağımlılık ekleme veya manuel bağımlılık ekleme adı verilir. Car
örneğinde yalnızca bir bağımlılık vardı ancak daha fazla bağımlılık ve sınıf, bağımlılıkların manuel olarak yerleştirilmesini daha sıkıcı hale getirebilir. Manuel bağımlılık ekleme de
çeşitli sorunlara yol açar:
Büyük uygulamalar için tüm bağımlılıkları almak ve bunları doğru şekilde bağlamak, büyük miktarda ortak metin kodu gerektirebilir. Çok katmanlı bir mimaride, bir üst katman için nesne oluşturmak üzere bunun altındaki katmanların tüm bağımlılıklarını sağlamanız gerekir. Somut bir örnek vermek gerekirse, gerçek bir araba yapmak için bir motor, şanzıman, şasi ve başka parçalara ihtiyacınız olabilir.
Bağımlılıkları aktarmadan önce oluşturamadığınız durumlarda (örneğin, geç başlatmalar veya uygulamanızın akışları için nesneleri kapsama gibi) bellekteki bağımlılıklarınızın ömrünü yöneten özel bir container (veya bağımlılık grafiği) yazıp sürdürmeniz gerekir.
Bağımlılık oluşturma ve sağlama sürecini otomatik hale getirerek bu sorunu çözen kitaplıklar vardır. Bunlar iki kategoriye ayrılır:
Çalışma zamanında bağımlılıkları bağlayan yansıma tabanlı çözümler.
Derleme zamanında bağımlılıkları bağlamak için kod oluşturan statik çözümler.
Dagger; Java, Kotlin ve Android'de kullanılan ve Google tarafından yönetilen popüler bir bağımlılık ekleme kitaplığıdır. Dagger, sizin için bağımlılık grafiği oluşturup yöneterek uygulamanızda DI'yi kullanmayı kolaylaştırır. Guice gibi yansıma tabanlı çözümlerin geliştirme ve performans sorunlarının çoğunu çözen, tamamen statik ve derleme zamanı bağımlılıkları sunar.
Bağımlılık yerleştirmeye alternatifler
Bağımlılık yerleştirmeye bir alternatif de hizmet bulucu kullanmaktır. Hizmet konum bulucunun tasarım kalıbı, sınıfların somut bağımlılıklardan ayrıştırılmasını da iyileştirir. Bağımlılıklar oluşturup depolayan ve daha sonra bu bağımlılıkları isteğe bağlı olarak sağlayan hizmet bulucu adlı bir sınıf oluşturuyorsunuz.
Kotlin
object ServiceLocator { fun getEngine(): Engine = Engine() } class Car { private val engine = ServiceLocator.getEngine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }
Java
class ServiceLocator { private static ServiceLocator instance = null; private ServiceLocator() {} public static ServiceLocator getInstance() { if (instance == null) { synchronized(ServiceLocator.class) { instance = new ServiceLocator(); } } return instance; } public Engine getEngine() { return new Engine(); } } class Car { private Engine engine = ServiceLocator.getInstance().getEngine(); public void start() { engine.start(); } } class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }
Hizmet konum bulucu kalıbı, öğelerin tüketilme şekli açısından bağımlılık yerleştirmeden farklıdır. Hizmet konum bulucu kalıbıyla, sınıflar kontrol sahibi olur ve nesnelerin yerleştirilmesini ister; bağımlılık eklemeyle uygulama, kontrole sahiptir ve gerekli nesneleri proaktif olarak ekler.
Bağımlılık yerleştirme ile karşılaştırıldığında:
Bir hizmet konum belirleyicinin gerektirdiği bağımlılıkların toplanması, tüm testlerin aynı global hizmet bulucuyla etkileşimde bulunması gerektiğinden kodun test edilmesini zorlaştırır.
Bağımlılıklar API yüzeyinde değil, sınıf uygulamasında kodlanır. Sonuç olarak, sınıfın dışarıdan neye ihtiyacı olduğunu bilmek daha zordur. Sonuç olarak,
Car
veya hizmet bulucuda bulunan bağımlılıklarda yapılan değişiklikler, referansların başarısız olmasına yol açarak çalışma zamanı veya test hatalarına neden olabilir.Uygulamanın tamamının kullanım ömrü dışında bir konuyu kapsamak istiyorsanız nesnelerin ömürlerini yönetmek daha zordur.
Android uygulamanızda Hilt'i kullanma
Hilt, Android'de bağımlılık yerleştirme için Jetpack tarafından önerilen kitaplıktır. Hilt, projenizdeki her Android sınıfı için container sağlayarak ve bunların yaşam döngülerini sizin yerinize otomatik olarak yöneterek uygulamanızda DI yapmanın standart bir yolunu tanımlar.
Hilt, Dagger'ın sağladığı derleme zamanı doğruluğu, çalışma zamanı performansı, ölçeklenebilirlik ve Android Studio desteğinden yararlanabilmek için popüler Dagger DI kitaplığının üzerine geliştirilmiştir.
Hilt hakkında daha fazla bilgi edinmek için Hilt ile Bağımlılık Yerleştirme konusuna bakın.
Sonuç
Bağımlılık ekleme, uygulamanıza aşağıdaki avantajları sağlar:
Sınıfların yeniden kullanılabilirliği ve bağımlılıkların ayrıştırılması: Bir bağımlılığın uygulamalarını değiştirmek daha kolaydır. Kontrolün tersine çevrilmesi sayesinde kodların yeniden kullanımı iyileştirildi. Sınıflar artık bağımlılıklarının nasıl oluşturulduğunu kontrol etmez, bunun yerine herhangi bir yapılandırmayla çalışır.
Yeniden düzenleme kolaylığı: Bağımlılıklar, API yüzeyinin doğrulanabilir bir parçası haline gelir. Böylece, uygulama ayrıntıları olarak gizlenmek yerine, nesne oluşturma zamanında veya derleme anında kontrol edilebilir.
Test kolaylığı: Bir sınıf bağımlılıklarını yönetmez. Bu nedenle, sınıfı test ederken farklı durumlarınızın tümünü test etmek için farklı uygulamalardan geçebilirsiniz.
Bağımlılık yerleştirmenin avantajlarını tam olarak anlamak için Manuel bağımlılık yerleştirme bölümünde gösterildiği gibi bunu uygulamanızda manuel olarak denemeniz gerekir.
Ek kaynaklar
Bağımlılık yerleştirme hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın.