DIP, IoC Kavramları ve Flutter’da IoC Container Kullanımı

Ahmet ÇELİK
4 min readMar 19, 2021

--

İleride karşılaşacağımız bir problemi kestiremeyebiliriz fakat birçok kişinin yılların verdiği deneyimle ortaya koyduğu yaklaşımlar ve çözüm yollarını kullanmak bizi bir adım öne taşıyacaktır.

Yaklaşımlara örnek olarak SOLID prensiplerini veya tasarım kalıplarını (design patterns) verebiliriz.

Bu yazımda Dependency Inversion (DIP), Inversion of Control (IoC) prensiplerini ve Flutter’da IoC Container kullanımından bahsedeceğim.

Flutter/Dart IoC Container

Dependency Inversion ve Inversion of Control Nedir?

Dependency derken? Yani X bir sınıfın içerisinde farklı bir sınıf veya ara yüze bağlı olması. Bu sınıf veya ara yüz olmadan X sınıfının işini yapamaması olarak açıklayabiliriz.

Dependency Inversion kavramı, kelime anlamı olarak bağımlılığın tersine çevrilmesini ifade eder. Uygulama olarak da iki temel kuralı vardır:

  • Yüksek seviyeli modüller, düşük seviyeli modüllere doğrudan bağlı olmamalıdır. Soyutlamalara bağlı olmalıdırlar.
  • Soyutlamalar ayrıntılara bağlı olmamalıdır. Ayrıntılar soyutlamalara bağlı olmalıdırlar.

Hemen Dependency Inversion ile ilgili senaryomuza geçelim:

Bir mobil uygulamanın lokal verilerinin tutulması için bugün sqlite veri tabanının kullanılması uygun görülüyor. Ancak ileriki zamanlarda tutulacak verinin modeli, iç içe dizilerden oluşan karışık bir hal alınca NoSQL bir veri tabanı olan sembast veri tabanına geçilmesi karar alınıyor.

Böyle bir senaryoda uygulama içerisindeki diğer modüller de sqlite’a göre davranırsa veri tabanını değiştirmek istediğimizde diğer tüm modülleri de değiştirmemiz gerekir. Ancak soyut bir arayüze göre davranırsa diğer modüllere dokunmadan başarılı bir şekilde veri tabanını değiştirmiş olacağız.

Yukarıdaki sınıf diyagramını konuşturacak olursam :)

UserService, “IUserDataSource’a uyan herhangi bir nesne bana yeter”.

UserSembastDataSource, “Sembast veri tabanına özel kodları içeriyorum ama ben de IUserDataSource ara yüzüne uygun yazıldım.”

UserSqliteDataSource ve UserSharedPrefDataSource da UserSembastDataSource gibi kendine has birbirinden bağımsız kodlar içerdiğini söyler ama bunların rastgele yazılmadığını IUserDataSource’a bağlı kalınarak yazıldığını belirtir.

Buradaki tüm konuşmalar ikili olarak IUserDataSource ile bir modül ile gerçekleşti. Yani uygulamamıza yeni bir veri tabanı eklemek istersek sadece IUserDataSource’a implemente etmemiz yeterli olacaktır.

Inversion of Control ise kelime anlamı olarak kontrolü tersine çevirmeyi ifade eder. Sınıfların birbirlerine olan bağımlılıklarını kontrol etme ve bunları merkezi bir yerden yönetme prensibini destekler. Uygulama tarafında bunu sağlayan yapıya da IoC Container denir. Flutter/Dart için kullanacağımız IoC Container da “kiwi” paketi olacaktır.

IoC Container’larda genel olarak 3 kavram bulunur:

  • Register: Hangi tür için hangi sınıftan instance üretileceğinin belirtildiği yapı
  • Resolve: IoC Container’dan instance çağırma işlemi
  • Unregister/Dispose: Register edilen bağımlılıkları yok etmek için kullandığımız yapı

Tamam, artık koda geçiyoruz :)

Flutter & Kiwi IoC Container

Flutter’da IoC Container olarak kiwi paketini kullanacağım. Siz de kurulumun ardından hemen başlayabilirsiniz.

İlk olarak Injector sınıfı oluşturup gelen isteklere karşılık hangi türde nesnelerin döneceğini belirtiyoruz. Aynı zamanda döndürülecek nesnenin bir defa mı yoksa her seferinde tekrardan mı üretilip üretilmeyeceğine de karar verebiliyoruz.

Uygulamamız ilk çalıştığında Injector sınıfımızdaki setup metodunu çalıştırarak IoC Container’imiz kullanıma hazır hale geliyor.

Container’dan bir instance mı kullanmak istiyoruz? Sorgun değil :)

final container = KiwiContainer();
container<IstedigimizTür>();

Şimdi çoğumuzun aklında tek bir soru var. O kadar dedik bir sınıf başka sınıftan nesne üretmemesi lazım. Her KiwiContainer kullanacağımız zaman yeni bir nesne mi üretelim şimdi? Bari singleton yapsaydık.

KiwiContainer sınıfının içeriğine bakarsak yukarıdaki gibi bir instance oluşturma işlemini zaten singleton yaptığını görebiliriz. Eğer ekstradan farklı bir instance oluşturmak istersek KiwiContainer.scoped(); şeklinde kullanmamız yeterli.

--

--

Ahmet ÇELİK

Mobil uygulama geliştirici, gündeme dair konular ve yazılım trendleriyle ilgili özgür bir yazılımcı.