Singleton Class Nedir? - Kotlin

Tuğçe Aras
5 min readApr 20, 2023

--

Selamlar 👋, gelelim projelerimizde kullanmayı sevdiğimiz ancak yanlış kullanıldığında istemediğimiz sonuçlara neden olan konuya. Başlayalım o halde ✨

Singleton class’ın nasıl oluşturulduğunu öğrenmeden önce Singleton Pattern’in ne olduğuna bakalım.

🔶 Singleton Pattern Nedir?

  • Sınıfın yalnızca tek bir nesnesini oluşturmak ve her yerde bu nesne üzerinden işlem yapılmasını sağlamak amacıyla kullanılan bir pattern’dir. Singleton pattern’i projemize uygulamak için Singleton Class’ın oluşturulması gerekmektedir.
  • Sınıfın tek bir nesnesinin oluşturulması demek, memory’de oluşturulan o nesne için alan ayrılması demektir. Ancak sürekli olarak sınıfın nesnesi oluşturulsaydı; oluşturulan nesne kadar memory’de, alanın işgal edilmesi demekti.
  • Singleton pattern aynı zamanda “anti pattern” dir. Çünkü static bir yapısı vardır. Dolayısıyla da memory’den ölmeyen nesnenin oluşturulması demektir. Ayrıca oluşturulan bu nesneye Garbage Collector’nda dokunamaması demektir. Static yapısından dolayı, nesne kullanılsada kullanılmasada memory’den silinemiyor.
  • Singleton pattern’nin yanlış kullanımı OutOfMemory” hatasına yani “Memory Leak”e neden olmaktadır. O yüzden kontrollü ve gerekli olduğu takdirde kullanılması gerekmektedir.

Memory Leak’in ne olduğunu bilmiyorsanız eğer sizi şöyle alalım 👇

NOT: Özellikle Singleton Class, NetworkService ya da DatabaseService,.. gibi sınıfın yalnızca tek bir instance’na ihtiyaç duyduğumuz durumlarda kullanılır. Sürekli olarak bu class’lardan instance oluşturmak performans kaybına neden olabilmektedir.

Kotlinde nasıl Singleton Class oluşturulduğuna geçmeden önce Java’da nasıldı ona bakalım.

🔶 Java’da Singleton Class Oluşturma

  • Adım adım şunlar uygulanır:

→ Private constructor’ın oluşturulması

→ Sınıfın private static instance’nın oluşturulması

→ Oluşturulan instance’ı dönen static metodun oluşturulması

Hemen bir kod örneği ile Java’da nasıl Singleton Class yapıldığına bakalım.

Pekii kodda gördüğümüz @Volatile ve @Synchronized anotasyonlarının tam olarak görevi nedir?

@Volatile Nedir?

  • Değişken üzerinde yapılan değişikliğin diğer thread’lerde de görünür kılmak için kullanılan bir anotasyondur. Yani bir thread tarafından değişken üzerinde yapılan bir değişiklik sonucu değişkenin güncel değerinin aynı anda diğer thread’lerin de görebilmesidir.
  • Bir değişken eğer birden fazla thread arasında paylaşılmıyorsa o zaman bu anotasyonun kullanılmasına gerek yoktur.

@Synchronized Nedir?

  • Birden fazla thread’in aynı anda kaynağa erişip çalışması yerine, işlemlerin sırayla gerçekleştirilmesini sağlar. Yani bir thread işini tamamlamadan diğer thread’e geçmeyi engeller.

NOT: Aynı zamanda bu @Volatile ve @Synchronized’ın kullanılmasının bir diğer nedeni de, multithread işlemlerde instance’ın birden fazla kez oluşturulmasını engellemektir.

Gelelim Kotlin’de nasıl Singleton Class oluşturduğumuza

🔶 Kotlin’de Singleton Class Oluşturma

  • Kotlin’de Singleton Class oluşturmak için object ve companion object keyword’leri kullanılmaktadır. Bu kadar 😳

Şimdi sırayla bunların ne olduklarına bakalım.

🔹 Object Declarations

  • @Volatile ya da @Synchronized kullanmadan sadece object keyword’nün kullanılmasıyla Singleton class oluşturulmaktadır.
  • “object” keyword’ü Java’da oluşturulan tüm o yapıları arka planda otomatik olarak kendisi yapmaktadır. Nasıl yani? 🤔

Hemen bir örnek kod ile gösterelim.

Show Kotlin ByteCode ile arka planda nasıl tutulduğuna bakalım şimdi de. (Tools -> Kotlin -> Show Kotlin ByteCode | Decompile’a tıklayın sonrasında)

  • İlk kez erişildiklerinde geç başlatılırlar. (lazy initialized)
  • Arka planda static olarak tutulurlar.
  • Miras olarak bir sınıfa verilemez.
  • “object” keyword’ü thread safe singleton’lar oluşturmamızı sağlar.

Object’in başka bir kullanım şekli daha vardır. 👇

🔹 Object Expression

  • İsimsiz class’lar, isimsiz nesneler ya da isimsiz interface’ler oluşturmamızı sağlar.
  • Kullanıldıkları yerde hemen initialize edilirler.
  • İçerisine yazılan property ya da fonksiyona erişim yapılamamaktadır. Nasıl yani? 😮

Örnek ile gösterelim.

Show Kotlin ByteCode ile arka planda nasıl tutulduğuna da bakalım.

  • Interface implement edebilmekteyiz ve interface’in içerisindeki fonksyonun override edilmesi zorunludur. Ayrıca object expression içerisindeki herhangi bir property ya da fonksiyona ulaşamazken implement ettiğimiz interface’in fonksiyonuna main fonksiyonumuz içerisinden ulaşabilmekteyiz.

NOT: Expression kullanımı yaparken tekil olarak bir class ya da interface’i object’in karşılığı olarak yazılıyorsa değişkenin tipini belirtmeye gerek yoktur. (Yukarıdaki örnekte yaptığımız gibi) Ancak birden fazla karşılık verilmişse bu durumda değişkenin tipinin belirtilmesi gerekmektedir. Aksi takdirde ide zaten bu durumla alakalı size hata verecektir.

Örnek ile gösterelim.

Gelelim Kotlin’de bir sınıfı Singleton Class yapmak için kullandığımız diğer keyword’e

🥁🥁🥁…

🔹 Companion Object

  • O sınıfa özgü bir nesnedir.
  • Class’ın tamamını değil belli bir kısmını static ve Singleton yapmak için kullanılır.

Yapısına bakacak olursak:

  • Yukarıdaki kod örneğinde dikkat ettiyseniz eğer companion object dışında kalan fonksiyona erişmek için sınıfın bir örneğini oluşturmak zorunda kaldık. Ancak companion object içindeki property ya da fonksiyona erişmek için class adını kullanmamız yeterli oldu.
  • Companion object kullanırken örnekte verdiğim gibi isim verip o şekilde de kullanabilirsiniz ya da isim vermeden direkt olarak da bir kullanım yapabilirsiniz.

Bir de Show Kotlin ByteCode’dan arka planda nasıl tutulduğuna bakalım.

Gelelim “const val” durumuna ❗ 👇

NOT: Normal class içerisinde const val tanımlayamazken, companion object içerisinde bu tanımı yapabilmekteyiz. Ama neden???

Eğer normal class içerisinde bir const val sabiti tanımlayabilseydik sınıftan oluşturulan her bir nesne ayrıca bellekte farklı alanlar kaplayacaktı. Ayrıca sınıfın her bir nesnesi için const val değeri farklı olabilirdi. Ancak companion object, sınıfın kendisiyle beraber bir örnek olarak var olur. Bu nedenle companion object içerisinde tanımlanan const val , sınıfın tüm nesneleri tarafından paylaşılan ve sabit olarak değerlendirilen bir özelliktir.

  • Class içerisindeki property ya da fonksiyonlara companion object içerisinden erişilememektedir.

Örnek koda bakalım.

url: tenor

--

--