JavaRush /Java Blog /Random-ID /Pola Desain: Singleton

Pola Desain: Singleton

Dipublikasikan di grup Random-ID
Halo! Hari ini kita akan memahami secara detail pola desain yang berbeda, dan kita akan mulai dengan pola Singleton, yang juga disebut “singleton”. Pola Desain: Singleton - 1Mari kita ingat: apa yang kita ketahui tentang pola desain secara umum? Pola desain adalah praktik terbaik yang dapat diikuti untuk memecahkan sejumlah masalah yang diketahui. Pola desain umumnya tidak terikat pada bahasa pemrograman apa pun. Anggap saja sebagai serangkaian rekomendasi, berikut ini Anda dapat menghindari kesalahan dan tidak menemukan kembali roda Anda.

Apa itu lajang?

Singleton adalah salah satu pola desain paling sederhana yang dapat diterapkan pada suatu kelas. Terkadang orang mengatakan “kelas ini adalah kelas tunggal”, artinya kelas ini mengimplementasikan pola desain tunggal. Terkadang perlu untuk menulis kelas yang hanya dapat membuat satu objek. Misalnya, kelas yang bertanggung jawab untuk mencatat atau menghubungkan ke database. Pola desain Singleton menjelaskan bagaimana kita dapat menyelesaikan tugas tersebut. Singleton adalah pola desain yang melakukan dua hal:
  1. Memberikan jaminan bahwa suatu kelas hanya akan memiliki satu instance kelas tersebut.

  2. Menyediakan titik akses global ke instance kelas ini.

Oleh karena itu, ada dua fitur yang menjadi ciri khas hampir setiap implementasi pola tunggal:
  1. Konstruktor swasta. Membatasi kemampuan untuk membuat objek kelas di luar kelas itu sendiri.

  2. Metode statis publik yang mengembalikan turunan kelas. Metode ini disebut getInstance. Ini adalah titik akses global ke instance kelas.

Opsi implementasi

Pola desain tunggal digunakan dengan cara yang berbeda. Setiap pilihan baik dan buruk dengan caranya sendiri. Di sini, seperti biasa: tidak ada yang ideal, tetapi Anda harus berjuang untuk itu. Namun pertama-tama, mari kita definisikan apa yang baik dan apa yang buruk, dan metrik apa yang memengaruhi evaluasi penerapan pola desain. Mari kita mulai dengan hal positif. Berikut kriteria yang membuat pelaksanaannya menarik dan menarik:
  • Inisialisasi lambat: ketika kelas dimuat saat aplikasi berjalan tepat pada saat dibutuhkan.

  • Kesederhanaan dan transparansi kode: metriknya tentu saja subjektif, tetapi penting.

  • Keamanan thread: berfungsi dengan benar di lingkungan multi-thread.

  • Kinerja tinggi dalam lingkungan multi-thread: thread saling memblokir secara minimal atau tidak sama sekali saat berbagi sumber daya.

Sekarang kontra. Mari kita buat daftar kriteria yang menunjukkan penerapan yang buruk:
  • Inisialisasi non-malas: ketika sebuah kelas dimuat saat aplikasi dimulai, terlepas dari apakah diperlukan atau tidak (paradoksnya, di dunia IT lebih baik bermalas-malasan)

  • Kompleksitas dan keterbacaan kode yang buruk. Metriknya juga subjektif. Kita asumsikan kalau keluar darah dari mata, pelaksanaannya biasa saja.

  • Kurangnya keamanan benang. Dengan kata lain, “bahaya benang”. Pengoperasian yang salah di lingkungan multi-utas.

  • Performa buruk dalam lingkungan multi-thread: thread saling memblokir sepanjang waktu atau sering kali saat berbagi sumber daya.

Kode

Sekarang kami siap mempertimbangkan berbagai opsi implementasi, dengan mencantumkan pro dan kontra:

Solusi Sederhana

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
Implementasi paling sederhana. Kelebihan:
  • Kesederhanaan dan transparansi kode

  • Keamanan benang

  • Performa tinggi dalam lingkungan multi-thread

Minus:
  • Bukan inisialisasi yang malas.
Dalam upaya untuk memperbaiki kelemahan terakhir, kami mendapatkan implementasi nomor dua:

Inisialisasi Malas

public class Singleton {
  private static Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Kelebihan:
  • Inisialisasi malas.

Minus:
  • Tidak aman untuk benang

Implementasinya menarik. Kami dapat menginisialisasi dengan malas, tetapi kami kehilangan keamanan thread. Tidak masalah: dalam implementasi nomor tiga kami menyinkronkan semuanya.

Aksesor Tersinkronisasi

public class Singleton {
  private static Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Kelebihan:
  • Inisialisasi malas.

  • Keamanan benang

Minus:
  • Performa buruk di lingkungan multi-thread

Besar! Dalam implementasi nomor tiga, kami mengembalikan keamanan thread! Benar, lambat... Sekarang metodenya getInstancesudah tersinkronisasi, dan Anda hanya dapat memasukkannya satu per satu. Faktanya, kita tidak perlu menyinkronkan seluruh metode, tetapi hanya bagian di mana kita menginisialisasi objek kelas baru. Tapi kita tidak bisa begitu saja membungkus synchronizedbagian yang bertanggung jawab untuk membuat objek baru dalam satu blok: ini tidak akan memberikan keamanan thread. Ini sedikit lebih rumit. Metode sinkronisasi yang benar diberikan di bawah ini:

Penguncian Diperiksa Ganda

public class Singleton {
    private static Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Kelebihan:
  • Inisialisasi malas.

  • Keamanan benang

  • Performa tinggi dalam lingkungan multi-thread

Minus:
  • Tidak didukung pada versi Java yang lebih rendah dari 1.5 (kata kunci volatil telah diperbaiki di versi 1.5)

Saya perhatikan bahwa agar opsi implementasi ini berfungsi dengan benar, diperlukan salah satu dari dua kondisi. Variabelnya INSTANCEharus berupa final, atau volatile. Implementasi terakhir yang akan kita bahas hari ini adalah Class Holder Singleton.

Pemegang Kelas Singleton

public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
Kelebihan:
  • Inisialisasi malas.

  • Keamanan benang.

  • Performa tinggi dalam lingkungan multi-thread.

Minus:
  • Untuk pengoperasian yang benar, perlu adanya jaminan bahwa objek kelas Singletondiinisialisasi tanpa kesalahan. Jika tidak, pemanggilan metode pertama getInstanceakan berakhir dengan kesalahan ExceptionInInitializerError, dan semua pemanggilan metode berikutnya akan gagal NoClassDefFoundError.

Implementasinya hampir sempurna. Dan malas, dan aman untuk thread, dan cepat. Namun ada nuansa yang dijelaskan dalam minusnya. Tabel perbandingan berbagai implementasi pola Singleton:
Penerapan Inisialisasi malas Keamanan benang Kecepatan multithread Kapan digunakan?
Solusi Sederhana - + Cepat Tidak pernah. Atau ketika inisialisasi malas tidak penting. Tapi tidak pernah lebih baik.
Inisialisasi Malas + - Tak dapat diterapkan Selalu ketika multithreading tidak diperlukan
Aksesor Tersinkronisasi + + Perlahan-lahan Tidak pernah. Atau ketika kecepatan bekerja dengan multithreading tidak menjadi masalah. Tapi tidak pernah lebih baik
Penguncian Diperiksa Ganda + + Cepat Dalam kasus yang jarang terjadi ketika Anda perlu menangani pengecualian saat membuat singleton. (ketika Class Holder Singleton tidak berlaku)
Pemegang Kelas Singleton + + Cepat Selalu ketika multithreading diperlukan dan ada jaminan bahwa objek kelas tunggal akan dibuat tanpa masalah.

Pro dan kontra dari pola Singleton

Secara umum, singleton melakukan apa yang diharapkan:
  1. Memberikan jaminan bahwa suatu kelas hanya akan memiliki satu instance kelas tersebut.

  2. Menyediakan titik akses global ke instance kelas ini.

Namun pola ini mempunyai kelemahan:
  1. Singleton melanggar SRP (Prinsip Tanggung Jawab Tunggal) - kelas Singleton, selain tanggung jawab langsungnya, juga mengontrol jumlah salinannya.

  2. Ketergantungan kelas reguler atau metode pada singleton tidak terlihat dalam kontrak publik kelas tersebut.

  3. Variabel global itu buruk. Singleton pada akhirnya berubah menjadi satu variabel global yang besar dan kuat.

  4. Kehadiran singleton mengurangi kemampuan pengujian aplikasi secara umum dan kelas yang menggunakan singleton pada khususnya.

Oke, semuanya sudah berakhir. Sekarang. Kami melihat pola desain tunggal. Sekarang, dalam percakapan seumur hidup dengan teman programmer Anda, Anda tidak hanya bisa mengatakan apa yang baik tentangnya, tetapi juga beberapa kata tentang apa yang buruk tentangnya. Semoga berhasil menguasai ilmu baru.

Bacaan tambahan:

Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION