Lajang di Jawa

1. Perkenalan

Dalam artikel singkat ini, kita akan membahas dua cara paling populer untuk mengimplementasikan Singletons di Java biasa.

2. Singleton Berbasis Kelas

Pendekatan yang paling populer adalah mengimplementasikan Singleton dengan membuat kelas reguler dan memastikannya memiliki:

  • Seorang konstruktor pribadi
  • Bidang statis yang berisi satu-satunya contoh
  • Metode pabrik statis untuk mendapatkan instance

Kami juga akan menambahkan properti info, untuk penggunaan nanti saja. Jadi, implementasi kami akan terlihat seperti ini:

public final class ClassSingleton { private static ClassSingleton INSTANCE; private String info = "Initial info class"; private ClassSingleton() { } public static ClassSingleton getInstance() { if(INSTANCE == null) { INSTANCE = new ClassSingleton(); } return INSTANCE; } // getters and setters }

Meskipun ini adalah pendekatan umum, penting untuk dicatat bahwa ini bisa menjadi masalah dalam skenario multithreading , yang merupakan alasan utama untuk menggunakan Singletons.

Sederhananya, ini dapat mengakibatkan lebih dari satu contoh, melanggar prinsip inti pola. Meskipun ada solusi penguncian untuk masalah ini, pendekatan kami selanjutnya memecahkan masalah ini di tingkat akar.

3. Enum Singleton

Ke depan, jangan membahas pendekatan menarik lainnya - yaitu menggunakan enumerasi:

public enum EnumSingleton { INSTANCE("Initial class info"); private String info; private EnumSingleton(String info) { this.info = info; } public EnumSingleton getInstance() { return INSTANCE; } // getters and setters }

Pendekatan ini memiliki serialisasi dan keamanan thread yang dijamin oleh implementasi enum itu sendiri, yang memastikan secara internal bahwa hanya instance tunggal yang tersedia, memperbaiki masalah yang ditunjukkan dalam implementasi berbasis kelas.

4. Penggunaan

Untuk menggunakan ClassSingleton kita, kita hanya perlu mendapatkan instance secara statis:

ClassSingleton classSingleton1 = ClassSingleton.getInstance(); System.out.println(classSingleton1.getInfo()); //Initial class info ClassSingleton classSingleton2 = ClassSingleton.getInstance(); classSingleton2.setInfo("New class info"); System.out.println(classSingleton1.getInfo()); //New class info System.out.println(classSingleton2.getInfo()); //New class info

Adapun EnumSingleton , kita dapat menggunakannya seperti Java Enum lainnya:

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance(); System.out.println(enumSingleton1.getInfo()); //Initial enum info EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance(); enumSingleton2.setInfo("New enum info"); System.out.println(enumSingleton1.getInfo()); // New enum info System.out.println(enumSingleton2.getInfo()); // New enum info

5. Kesalahan Umum

Singleton adalah pola desain yang tampak sederhana, dan ada beberapa kesalahan umum yang mungkin dilakukan oleh programmer saat membuat singleton.

Kami membedakan dua jenis masalah dengan lajang:

  • eksistensial (apakah kita membutuhkan singleton?)
  • implementasional (apakah kita menerapkannya dengan benar?)

5.1. Masalah Eksistensial

Secara konseptual, singleton adalah sejenis variabel global. Secara umum, kita tahu bahwa variabel global harus dihindari - terutama jika statusnya bisa berubah.

Kami tidak mengatakan bahwa kami tidak boleh menggunakan lajang. Namun, kami mengatakan bahwa mungkin ada cara yang lebih efisien untuk mengatur kode kami.

Jika implementasi metode bergantung pada objek tunggal, mengapa tidak meneruskannya sebagai parameter? Dalam kasus ini, kami secara eksplisit menunjukkan pada apa metode tersebut bergantung. Akibatnya, kami dapat dengan mudah memalsukan dependensi ini (jika perlu) saat melakukan pengujian.

Misalnya, lajang sering digunakan untuk mencakup data konfigurasi aplikasi (yaitu, koneksi ke repositori). Jika mereka digunakan sebagai objek global, akan menjadi sulit untuk memilih konfigurasi untuk lingkungan pengujian.

Oleh karena itu, saat kami menjalankan pengujian, database produksi dimanjakan dengan data pengujian, yang hampir tidak dapat diterima.

Jika kita membutuhkan singleton, kita mungkin mempertimbangkan kemungkinan untuk mendelegasikan instansinya ke kelas lain - semacam pabrik - yang harus memastikan bahwa hanya ada satu instance singleton yang sedang dimainkan.

5.2. Masalah Implementasi

Meskipun lajang tampak cukup sederhana, penerapannya mungkin mengalami berbagai masalah. Semua menghasilkan fakta bahwa kita mungkin akan memiliki lebih dari satu contoh kelas.

Sinkronisasi

Implementasi dengan konstruktor pribadi yang kami sajikan di atas tidak aman untuk utas: ini bekerja dengan baik di lingkungan utas tunggal, tetapi dalam lingkungan multi utas, kami harus menggunakan teknik sinkronisasi untuk menjamin atomicity operasi:

public synchronized static ClassSingleton getInstance() { if (INSTANCE == null) { INSTANCE = new ClassSingleton(); } return INSTANCE; }

Perhatikan kata kunci yang disinkronkan dalam deklarasi metode. Badan metode memiliki beberapa operasi (perbandingan, pembuatan contoh, dan pengembalian).

Jika tidak ada sinkronisasi, ada kemungkinan bahwa dua utas interleave eksekusinya sedemikian rupa sehingga ekspresi INSTANCE == null mengevaluasi ke true untuk kedua utas dan, sebagai hasilnya, dua contoh ClassSingleton dibuat.

Sinkronisasi dapat mempengaruhi kinerja secara signifikan. Jika kode ini sering dipanggil, kita harus mempercepatnya menggunakan berbagai teknik seperti inisialisasi yang lambat atau penguncian yang diperiksa dua kali (perhatikan bahwa ini mungkin tidak berfungsi seperti yang diharapkan karena pengoptimalan compiler). Kita bisa melihat lebih detail dalam tutorial kita "Penguncian dengan Cek Ganda dengan Singleton".

Beberapa Contoh

Ada beberapa masalah lain dengan singleton yang terkait dengan JVM itu sendiri yang dapat menyebabkan kita berakhir dengan beberapa instance singleton. Masalah ini cukup halus, dan kami akan memberikan penjelasan singkat untuk masing-masingnya:

  1. Singleton seharusnya unik per JVM. Ini mungkin menjadi masalah untuk sistem terdistribusi atau sistem yang internal didasarkan pada teknologi terdistribusi.
  2. Setiap pemuat kelas mungkin memuat versi singleton-nya.
  3. Singleton mungkin akan dikumpulkan dari sampah setelah tidak ada yang memiliki referensi padanya. Masalah ini tidak menyebabkan adanya beberapa instance tunggal sekaligus, tetapi saat dibuat ulang, instance tersebut mungkin berbeda dari versi sebelumnya.

6. Kesimpulan

Dalam tutorial singkat ini, kami fokus pada bagaimana mengimplementasikan pola Singleton hanya menggunakan inti Java, dan bagaimana memastikannya konsisten dan bagaimana menggunakan implementasi ini.

Implementasi lengkap dari contoh-contoh ini dapat ditemukan di GitHub.