Kotlin ile "Instance" Oluşturma
Bu yazıda Constructor
‘lardan ve class instance’ları oluşturmanın farklı yöntemlerinden bahsedeceğim.
Kotlin’de sınıflar bir tane primary constructor
‘a ve bir veya birden fazla secondary constructor
‘a sahip olabilirler. Aşağıda 2 parametre alan (name ve speed) primary constructor örneği görüyorsunuz. Primary constructor tanımı class isminin hemen ardından başlar. Constructor parametrelerinin başındaki val
ve var
sayesinde Kotlin compiler name ve speed adında property’leri otomatik olarak oluşturur.
Primary constructor’in tüm parametreleri için default değerler tanımlanırsa kotlin compiler parametre almayan ve field’lara default değerleri atayan bir constructor daha yaratır.
Primary construct içine kod yazılmaz. Init işlemleri için aşağıdaki örnekte olduğu gibi initializer block
‘lar kullanılır.
Secondary constructor’lar birden fazla olabilir ve tanımlanırken aşağıdaki örnekteki gibi constructor
keyword kullanılır. Class’ın bir primary constructor’ı varsa primary constructor’a this
ile gönderme yapmak gerekir:
Static Factory Method
Aksini belirtmediğimiz sürece Kotlin’de constructor’lar public
‘tir. Aşağıdaki örnekte constructor’ı gizlenmiş bir class örneği görebilirsiniz:
Bu örnekte, oluşturmak istedigimiz sınıfın instance’ını dönen public static factory method
‘lar görüyorsunuz. Constructor private olduğu için Spell
oluşturmak istedigimiz zaman bu factory method’ları kullanmamız gerekir. Static factory method’ların Effective Java Item 1
‘de anlatılan avantajları şunlar:
- Static factory method’lara anlamlı isimler verebiliriz.
acidSplash
veinvisibility
gibi. - Constructor’lar her çağırıldıklarında yeni bir instance oluşur, fakat static factory method’lar her çağırıldıklarında yeni bir instance yaratmak zorunda değildir. Aşağıdaki örnekte olduğu gibi daha önceden yaratılmış ve cache’lenmiş bir instance dönebilirler.
Joshua Bloch tarafından yazılan Effective Java kitabını mutlaka bulun okuyun, çok ciddiyim. Hatta henüz bu kitabı okumadıysanız bırakın bu yazıyı okumayı kitabın bir kopyasını edinin ve okumaya başlayın.
Yukarıdaki örnek’te Kotlin’e özel iki özellik var:
1. Lazy : Delegated properties
Error sınıfının errors field’ının oluşması lazy
tanımlandı. Bu şu demek oluyor, errors adındakı map
‘e ilk kez erişildiğinde yeni bir map instance yaratılır ve daha sonraki erişimlerde bu instance kullanılır. Bu mekanizmaya Kotlin’de Delegated Properties adı veriliyor. Delegate’lerden başka bir yazıda daha detaylı bahsedeceğim.
2. Zengin Standart Library
mutableMapOf
ve Colletions.getOrPut
Kotlin’in zengin standart library’si içindeki metodlardan sadece ikisi. Standart library fonksiyonları ile ilgili ayrı bir yazı yazmak istiyorum.
Builder Pattern
Karmaşık sınıfların instance’larını oluşturmak istediğimizde bir çok opsiyonel parametresi olan constructor’lar ya da static factory method’lar iyi bir çözüm olmaz. Böyle durumlarda Builder
tasarım kalıbını kullanabiliriz.
Yine bir örnek üzerinden gidelim. Kullanıcılara uyarı mesajı göstermek için kullanacağımız bir Dialog
sınıfımız olsun. Başlık, mesaj, renkler ve dialog kapatıldığında çalıştırmak istediğimiz bir code bloğunu parametre olarak alan bir constructor aşağıdaki gibi tanımlanabilir.
Sadece title
ve message
parametreleri verip default renklerde ve kapatıldığında ekstra birşey yapmasını istemediğimiz bir dialog oluşturmak istersek aşağıdaki gibi bir secondary constructor ekleyebiliriz.
Şimdi dialog oluşturmak biraz daha kolay. Fakat bu sefer de farklı renklerde dialog’lar oluşturmak istersek yeni constructor’lar eklemek zorunda kalacağız ya da 5 tane parametre alan primary constructor’ı kullanmaya devam edeceğiz. Dialog sınıfına zamanla yeni özellikler eklediğimizde (ikon, açılma animasyonu, kapanma animasyonu gibi…) client’ların constructor ile dialog oluşturması karmaşıklaşmaya başlar. Tam burada Builder
pattern yardımımıza koşuyor.
Kotlin’de builder yazmanın farklı yöntemleri var. Aşağıdaki örnekte extension
metodlarını kullanan DialogBuilder
sınıfı var. Bu builder sayesinde:
- Client’lar icin dialog oluşturmak kolaylaştı ve client kodu daha okunaklı oldu.
- Dialog sınıfına opsiyonel property’ler eklemek daha kolay oldu.
Yukarıdaki örnekte 31. satırdaki build metodunun 3. parametresini beraber inceleyelim:
init: DialogBuilder.() -> Unit
. init
adındaki bu function’ın tipi DialogBuilder.() -> Unit
‘dir. Kotlin jargon’unda bu tür fonksiyonlara function type with receiver
deniliyor. Linkteki dokümanda detayları anlatılan bu tip fonsiyonlar Type Safe Builder
‘ları yazmaya olanak sağlar.
Tekrar bizim örneğimize dönecek olursak; init
fonksiyonu parametre olarak DialogBuilder instance’i (receiver) istiyor ve bu instance’in public metodlarına ve property’lerine init
fonksiyonu içinden erişebiliyoruz. 40, 41 ve 42. satırlarda yapılan işlemlerin receiver
‘i DialogBuilder olduğu için titleColor = Color.RED
dediğimiz zaman DialogBuilder’ın title
field’ını güncellemiş oluyoruz.
Son olarak, aşağıdaki kullanım örneklerine bakarak, builder pattern’in karmaşık instance’ları oluştururken okuması ve kullanımı kolay kod yazmamıza yardım ettiğini açıkça görebiliriz.