Crocusoft | Android-də Design Pattern’lər: Singleton Pattern
purple android logo
Bloqlar 5 MIN READ 25.05.2022 12:33:30

Android-də Design Pattern’lər: Singleton Pattern

Developerlər arasında tez-tez “if it is working, don’t touch it” və ya “... but it is running” kimi fikirlər səsləndirilir:) 

Yəni, bir çox proqramçı tez-tələsik kodu yazıb sadəcə işləməyinin dərdindədir. Lakin, kodun səliqəli və optimal yazılması vacib məsələlərdən biridir. Optimal kod dedikdə mümkün qədər az yaddaş resursu işlədən və zamana görə sürətli kod nəzərdə tutulur. 

Bu gün biz optimal və səliqəli kod yazmaqda əsas köməkçi vasitələrdən biri olan design pattern-lərə giriş edərək, Singleton pattern-dən bəhs edəcəyik.

Singleton Pattern nədir?

Singleton pattern tətbiq edilmiş class-larda onun instance-nın(nüsxə/obyekt) yaradılması limitlənir və yalnız bir obyekti yaradılır. Bu obyektin qlobal referance-ı da o class tərəfindən təmin edilir. Digər classlar və ya istifadəçi bu class-ın obyekti üçün request göndərəndə eyni obyekti əldə etmiş olurlar.

Singleton Class-ın faydaları nələrdir?

İstənilən android tətbiqində, adətən, müxtəlif classların obyekti üçün biz sadəcə bir qlobal instance-a ehtiyac duyuruq. Məsələn, OkHttpClient, HttpLoggingInterceptor, Retrofit, Gson, SharedPreferences 

Əgər biz hər dəfə bu class-lara ehtiyac duyarkən onların obyektini yaratsaq müxtəlif problemlərlə qarşılaşarıq. Məsələn: Tətbiqdə state-in itirilməsi, əlavə yaddaş sərfi, zaman itkisi və digər xoşagəlməz hallar. Lakin, Singleton class vasitəsilə biz əlavə instance yaradılmasının qarşısını alaraq lazımsız resurs istifadəsini əngəlləmiş oluruq.

Singleton classların sintaksisi ilə tanış olaq:

Basic implementation-dan başlayaraq ən optimal varianta qədər bir-bir nəzərdən keçirək:

Aşağıdakı kod nümunəsi Singleton class-ın yaradılmasını nümayiş etdirir.

public class Singleton  {

 

    private static Singleton INSTANCE = null;

 

    // other instance variables can be here

     

    private Singleton() {};

 

    public static Singleton getInstance() {

        if (INSTANCE == null) {

            INSTANCE = new Singleton();

        }

        return(INSTANCE);

    }

     

    // other instance methods can follow 

}

 

Yuxardakı nümunədə class-ın instance-nı saxlamaq üçün static INSTANCE referance-mız var. Eyni zamanda class-ın konstruktorunu private etməklə onun obyektinin yaradılmasını qadağan etmişik. Class yalnız özü öz intance-nı (nüsxəsini) yarada bilər. Static getInstance() methodu class-ın obyetinin yaradılmasını təmin edir. Əgər INSTANCE null olarsa, obyekti yaradılır və return edir.

Nümunə: Retrofit üzərində Singleton Pattern

Bildiyimiz kimi Retrofit REST web service-lərə connect olaraq APİ-ları Java interface-lərinə çevirən populyar kitabxanalardan biridir. 

Android tətbiqlərdə network requestlər edə bilməyimiz üçün retrofit obyektin yalnız bir qlobal instance-nın əlimizdə olması kifayət edər. Belə ki, müxtəlif screenlərdə (Məsələn, UserActivity, SettingsActivity və s.) hər dəfə yeni obyekt yaradıb requesti atmaq əlavə resurs istifadəsi sayılır. Bu bizi lazımsız obyekt yaratmaqdan, əlavə yaddaş sərfindən xilas edər.

import retrofit2.Retrofit;

import retrofit2.converter.gson.GsonConverterFactory;

  

public class RetrofitClient {

  

    private static Retrofit retrofit = null;

  

    public static Retrofit getClient(String baseUrl) {

        if (retrofit==null) {

            retrofit = new Retrofit.Builder()

                    .baseUrl(baseUrl)

                    .addConverterFactory(GsonConverterFactory.create())

                    .build();

        }

        return retrofit;

    }

}

 

 

Beləliklə, A istifadəçisi Retrofit.getInstance() methodunu çağırarkən əgər hələ heç bir obyekt yaradılmayıbsa getInstance() onu yaradır. Növbəti dəfə B istifadəçisi getInstance()-ı çağırarkən artıq referance null olmadığından yeni obyekt yaradılmır, əvəzində köhnə yaradılmış elə həmin obyekt qaytarılır.

Multithread-lərdə Singleton Pattern

Android programlaşdırmada eyni anda paralel olaraq müxtəlif əməliyyatları yerinə yetirmək üçün thread-lərdən istifadə edilir. Lakin, bu bəzən Singleton məntiqini poza bilər. Bu necə baş verir? Fərz edək ki, iki threadimiz var və bunların hər ikisi Singleton classının getInstance() methodunu çağırır. Bu zaman onlar eyni anda işləyərsə iki müxtəlif obyekt yaradıla bilər ki bu da Singleton məntiqini pozar. Bunun qarşısını almaq üçün Synchronize məntiqindən istifadə edilir:

Burada “thread safe” anlayışı daxil edilir. Belə ki, syncronize keywordu methoda əlavə edilərsə o method thread safe sayılır. Yəni, syncronize olunmuş method bir thread tərəfindən çağrılarsa digər threadler bloklanir. Və tasklar bir növ ardıcıl yerinə yetirilir. (sinxronlaşdırılır)

public class Singleton  {

 

    private static Singleton INSTANCE = null;

 

    // other instance variables can be here

     

    private Singleton() {};

 

    public static synchronized Singleton getInstance() {

        if (INSTANCE == null) {

            INSTANCE = new Singleton();

        }

        return(INSTANCE);

    }

     

    // other instance methods can follow 

}

 

 

Singleton Classı necə yazmalı?

Optimal Singleton class yazmaq vacib məsələlərdən biridir. Ümumiyyətlə, bunun bir neçə üsulu var. Bəzilərini nəzərdən keçirək:

  1. Eager initialization:

Bu üsulun mahiyyəti ondan ibarətdir ki, class-ın instance-ı class yüklənəndə yaradılır. Nümunəyə nəzər yetirək:

public class SingletonClass {

 

    private static volatile SingletonClass sSoleInstance = new SingletonClass();

 

    //private constructor.

    private SingletonClass(){}

 

    public static SingletonClass getInstance() {

        return sSoleInstance;

    }

}

 

Bu üsul zamanı class yüklənərkən artıq obyekt yaradılmış olur. Belə ki, static dəyişənlər ilk initialize olunan və class sonlanana kimi yaddaşdan silinməyən dəyişənlərdir. Bu səbəbdən istənilən halda (hətta ona ehtiyac duymasaq belə) obyekt yaradılmış olur ki, bu da artıq resurs sərfinə səbəb ola bilər.

 

  1. Lazy İnitialization:

 

public class SingletonClass {

 

    private static SingletonClass sSoleInstance;

 

    private SingletonClass(){}  //private constructor.

 

    public static SingletonClass getInstance(){

        if (sSoleInstance == null){ //if there is no instance available... create new one

            sSoleInstance = new SingletonClass();

        }

 

        return sSoleInstance;

    }

}

 

Koddan da göründüyü kimi burada obyekt yalnız getInstance() methodu ilk dəfə çağrılarkən yaradılır. Yox əgər ilk dəfə deyilsə (yəni, field artıq initialize olunubsa) mövcud obyekti qaytarır. 

 

  1. Singleton Classı thread safe yazmalı:

Yuxarda qeyd edildiyi kimi getInstance() methodunu syncronize yazsaq onu thread safe etmiş oluruq. Bunun sayəsində ondan istifadə edən threadlər müəyyən ardıcıllıqla onu çağıra biləcək. Nəticədə, Singleton məntiqi pozulmayacaq.

public class SingletonClass {

 

    private static SingletonClass sSoleInstance;

 

    //private constructor.

    private SingletonClass(){

       

        //Prevent form the reflection api.

        if (sSoleInstance != null){

            throw new RuntimeException("Use getInstance() method to get the single instance of this class.");

        }

    } 

 

    public synchronized static SingletonClass getInstance(){

        if (sSoleInstance == null){ //if there is no instance available... create new one

            sSoleInstance = new SingletonClass();

        }

 

        return sSoleInstance;

    }

}

 

Singleton Classın yalnız bir instance-nın olduğunu necə yoxlamalı?

 

Bildiyimiz kimi Javada əgər iki obyekt eynidirsə onda onların hash kodu eyni olacaqdır. Deməli, getInstance() methodunun qaytardığı obyektlərin hash code-nu müqaisə etməklə nəticəni yoxlaya bilərik: 

public class SingletonTester {

   public static void main(String[] args) {

        //Instance 1

        SingletonClass instance1 = SingletonClass.getInstance();

 

        //Instance 2

        SingletonClass instance2 = SingletonClass.getInstance();

 

        //now lets check the hash key.

        System.out.println("Instance 1 hash:" + instance1.hashCode());

        System.out.println("Instance 2 hash:" + instance2.hashCode());  

   }

}

 

Singleton Class androiddə ən çox tətbiq olunan pattern-lərdən biridir. O əsasən məlumat bazası ilə əlaqə, shared preference və s. məsələlərdə geniş tətbiq olunur və effektiv nəticələr verir. 

Müəllif: Aytac Dadaşova