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ştirmeden bahsetmeden önce, bu sayfada bağımlılık yerleştirmenin işleyiş şekliyle ilgili genel bir 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, ihtiyaç duyduğu bir nesneyi üç şekilde alabilir:
- 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. - Verinin parametre olarak sağlanmasını 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şturucu, parametre olarakEngine
değerini 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 bağımlılıklarını elde etmesini sağlamak yerine bunları sağlarsınız.
Bir örnekle açıklayalım. Bağımlılık ekleme olmadan, kodda kendi Engine
bağımlılığını oluşturan bir Car
temsil edilir:
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(); } }
![Bağımlılık enjeksiyonu yapılmayan araç sınıfı](https://developer.android.google.cn/static/images/training/dependency-injection/1-car-engine-no-di.png?authuser=5&hl=tr)
Car
sınıfı kendi Engine
öğesini oluşturduğundan bu, bir bağımlılık yerleştirme örneği değildir. Bu, aşağıdaki nedenlerle sorunlu olabilir:
Car
veEngine
birbiriyle sıkı sıkıya bağlıdır.Car
örneği, birEngine
türü kullanır ve hiçbir alt sınıf ya da alternatif uygulama kolayca kullanılamaz.Car
kendiEngine
yapısını oluşturacak olsaydı yalnızcaGas
veElectric
türündeki motorlar için aynıCar
öğesini yeniden kullanmak yerine iki türCar
oluşturmanız gerekirdi.Engine
ürününün aşırı bağımlılığı, testi daha zor hale getirir.Car
, gerçek birEngine
örneğini kullandığından farklı test durumları içinEngine
öğesini değiştirmek üzere test yinelemesi 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 bir 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(); } }
![Bağımlılık enjeksiyonu kullanılan araba sınıfı](https://developer.android.google.cn/static/images/training/dependency-injection/1-car-engine-di.png?authuser=5&hl=tr)
main
işlevi, Car
değerini kullanır. Car
, Engine
öğesine bağlı olduğundan uygulama, Engine
örneği oluşturur ve daha sonra bunu Car
örneği oluşturmak için kullanır. DI tabanlı bu yaklaşımın avantajları:
Car
yeniden kullanılabilir.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ğini iletmektir.Car
başka değişiklik yapmadan çalışmaya devam eder.Car
kolayca test edilir. 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 yöntemdir. Sınıfın bağımlılıklarını kurucuya aktarırsınız.
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, bağımlılıklar sınıf oluşturulduktan sonra örneklendirilir. 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 elle bağımlılık ekleme veya manuel bağımlılık yerleştirme denir. Car
örneğinde yalnızca tek 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 yerleştirme de
çeşitli sorunlara yol açar:
Büyük uygulamalarda, tüm bağımlılıkları almak ve bunları doğru şekilde bağlamak büyük miktarda standart kod gerektirebilir. Çok katmanlı bir mimaride, üst katman için bir nesne oluşturmak üzere 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 motora, şanzımana, şaseye ve başka parçalara ihtiyacınız olabilir. Bir motorun da silindirlere ve bujilere ihtiyacı olabilir.
Bağımlılıkları iletmeden önce oluşturamadığınızda (örneğin, geç başlatmalar veya uygulamanızın akışları için nesneleri kapsama aldığınızda) bellekteki bağımlılıklarınızın kullanım ömrünü yöneten özel bir kapsayıcı (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ğini 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 ele alan, tamamen statik ve derleme zamanı bağımlılıkları sunar.
Bağımlılık yerleştirme alternatifleri
Bağımlılık yerleştirmeye alternatif olarak hizmet bulucu kullanabilirsiniz. Hizmet bulucu tasarım deseni, sınıfların somut bağımlılıklardan ayrılmasını da iyileştirir. Bağımlılıkları oluşturan ve depolayan, ardından talep üzerine bu bağımlılıkları sağlayan hizmet bulucu adlı bir sınıf oluşturursunuz.
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 belirleyicisi kalıbı, öğelerin tüketilme şekli açısından bağımlılık yerleştirme işleminden farklıdır. Hizmet bulucu kalıbıyla sınıflar kontrol sahibi olur ve nesnelerin yerleştirilmesini ister; bağımlılık yerleştirme ile 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 bulucunun gerektirdiği bağımlılıkların toplanması, tüm testlerin aynı küresel 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ıfa dışarıdan ne gerektiğini 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 yol açabilir.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'in önerdiği kitaplıktır. Hilt, projenizdeki her Android sınıfı için container'lar sağlayarak ve bunların yaşam döngülerini sizin için otomatik olarak yöneterek uygulamanızda DI işlemini 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 yararlanmak için popüler DI kitaplığı Dagger'ın üzerine kurulmuştur.
Hilt hakkında daha fazla bilgi edinmek için Dependency Injection with Hilt (Hilt ile Bağımlılık Yerleştirme) sayfasına bakın.
Sonuç
Bağımlılık yerleştirme, 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ı: Bağımlılık uygulamalarını değiştirmek daha kolaydır. Kontrolün tersine çevrilmesi sayesinde kodların yeniden kullanımı iyileştirilir. 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 zamanında kontrol edilebilir.
Test kolaylığı: Bir sınıf bağımlılıklarını yönetmez. Bu nedenle, test ederken farklı durumlardan geçerek tüm farklı durumlarınızı test edebilirsiniz.
Bağımlılık yerleştirmenin avantajlarını tam olarak anlamak için bunu, Manuel bağımlılık yerleştirme bölümünde gösterildiği gibi 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.