Saya sedang membaca artikel Singleton di Wikipedia dan saya menemukan contoh ini:
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Walaupun saya sangat menyukai cara Singleton ini berperilaku, saya tidak dapat melihat bagaimana mengadaptasinya untuk memasukkan argumen ke konstruktor. Apa cara yang disukai untuk melakukan ini di Jawa? Apakah saya harus melakukan sesuatu seperti ini?
public class Singleton
{
private static Singleton singleton = null;
private final int x;
private Singleton(int x) {
this.x = x;
}
public synchronized static Singleton getInstance(int x) {
if(singleton == null) singleton = new Singleton(x);
return singleton;
}
}
Terima kasih!
Sunting: Saya pikir saya telah memulai badai kontroversi dengan keinginan saya untuk menggunakan Singleton. Biarkan saya menjelaskan motivasi saya dan semoga seseorang dapat menyarankan ide yang lebih baik. Saya menggunakan kerangka kerja komputasi grid untuk menjalankan tugas secara paralel. Secara umum, saya memiliki sesuatu seperti ini:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private final ReferenceToReallyBigObject object;
public Task(ReferenceToReallyBigObject object)
{
this.object = object;
}
public void run()
{
// Do some stuff with the object (which is immutable).
}
}
Apa yang terjadi adalah bahwa meskipun saya hanya meneruskan referensi ke data saya ke semua tugas, ketika tugas-tugas bersambung, data akan disalin berulang-ulang. Yang ingin saya lakukan adalah membagikan objek di antara semua tugas. Secara alami, saya dapat memodifikasi kelas seperti:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private static ReferenceToReallyBigObject object = null;
private final String filePath;
public Task(String filePath)
{
this.filePath = filePath;
}
public void run()
{
synchronized(this)
{
if(object == null)
{
ObjectReader reader = new ObjectReader(filePath);
object = reader.read();
}
}
// Do some stuff with the object (which is immutable).
}
}
Seperti yang Anda lihat, bahkan di sini saya memiliki masalah bahwa melewati jalur file yang berbeda tidak ada artinya setelah yang pertama dilewati. Inilah sebabnya saya menyukai ide untuk toko yang diposting dalam jawaban. Bagaimanapun, daripada memasukkan logika untuk memuat file dalam metode run, saya ingin abstrak logika ini menjadi kelas Singleton. Saya tidak akan memberikan contoh lain, tetapi saya harap Anda mendapatkan idenya. Tolong biarkan saya mendengar ide Anda untuk cara yang lebih elegan untuk mencapai apa yang saya coba lakukan. Terima kasih lagi!
Jawaban:
Saya akan menjelaskan maksud saya: singleton dengan parameter bukan singleton .
Singleton, menurut definisi, adalah objek yang Anda inginkan tidak lebih dari satu kali. Jika Anda mencoba memberi makan parameter ke konstruktor, apa gunanya singleton?
Anda memiliki dua opsi. Jika Anda ingin singleton diinisialisasi dengan beberapa data, Anda dapat memuatnya dengan data setelah instantiation , seperti:
Jika operasi yang dilakukan singleton Anda berulang, dan dengan parameter yang berbeda setiap kali, Anda mungkin juga meneruskan parameter ke metode utama yang dijalankan:
Bagaimanapun, instantiasi akan selalu menjadi parameter-kurang. Kalau tidak, lajang Anda bukan lajang.
sumber
Saya pikir Anda memerlukan sesuatu seperti pabrik untuk memiliki objek dengan berbagai parameter instantiated dan digunakan kembali. Ini dapat diimplementasikan dengan menggunakan sinkronisasi
HashMap
atauConcurrentHashMap
memetakan parameter (Integer
contohnya) ke kelas parameterabel 'tunggal' Anda.Meskipun Anda mungkin sampai pada titik di mana Anda harus menggunakan kelas non-singleton reguler (sebagai gantinya membutuhkan 10.000 singleton dengan parametrized berbeda).
Berikut adalah contoh untuk toko tersebut:
Untuk mendorongnya lebih jauh, Java
enum
juga dapat dianggap (atau digunakan sebagai) lajang parametrized, meskipun hanya memperbolehkan varian statis nomor tetap.Namun, jika Anda membutuhkan solusi 1 terdistribusi , pertimbangkan beberapa solusi caching lateral. Misalnya: EHCache, Terracotta, dll.
1 dalam arti menjangkau beberapa VM di beberapa komputer.
sumber
Anda dapat menambahkan metode inisialisasi yang dapat dikonfigurasi untuk memisahkan instantiation dari mendapatkan.
Kemudian Anda dapat menelepon
Singleton.init(123)
sekali untuk mengkonfigurasinya, misalnya di startup aplikasi Anda.sumber
Anda juga dapat menggunakan pola Builder jika Anda ingin menunjukkan bahwa beberapa parameter wajib.
Kemudian Anda dapat membuat / membuat instance / parametrized sebagai berikut:
sumber
Pernyataan " singleton dengan parameter bukan singleton " tidak sepenuhnya benar . Kita perlu menganalisis ini dari perspektif aplikasi daripada dari perspektif kode.
Kami membangun kelas tunggal untuk membuat satu instance objek dalam satu aplikasi dijalankan. Dengan memiliki konstruktor dengan parameter, Anda dapat membangun fleksibilitas ke dalam kode Anda untuk mengubah beberapa atribut objek tunggal Anda setiap kali Anda menjalankan aplikasi Anda. Ini bukan pelanggaran pola Singleton. Sepertinya ini pelanggaran jika Anda melihatnya dari perspektif kode.
Pola Desain ada untuk membantu kita menulis kode yang fleksibel dan dapat diperpanjang, bukan untuk menghalangi kita menulis kode yang baik.
sumber
Gunakan getter dan setter untuk mengatur variabel dan menjadikan konstruktor default pribadi. Kemudian gunakan:
sumber
Terkejut bahwa tidak ada yang menyebutkan bagaimana logger dibuat / diambil. Sebagai contoh, di bawah ini menunjukkan bagaimana Log4J logger diambil.
Ada beberapa tingkatan tipuan, tetapi kuncinya adalah di bawah metode yang cukup banyak menceritakan segalanya tentang cara kerjanya. Ini menggunakan tabel hash untuk menyimpan logger yang ada dan kunci berasal dari nama. Jika logger tidak ada untuk memberi nama, logger menggunakan pabrik untuk membuat logger dan kemudian menambahkannya ke tabel hash.
sumber
Modifikasi pola Singleton yang menggunakan inisialisasi Bill Pugh pada idiom pemegang permintaan . Ini adalah utas aman tanpa overhead konstruksi bahasa khusus (yaitu volatile atau disinkronkan):
sumber
finally { RInterfaceHL.rloopHandler = null; }
masukgetInstance
, karena referensi statis dapat menyebabkan kebocoran memori jika kita tidak hati-hati. Dalam kasus Anda sepertinya itu bukan masalah, tapi saya bisa membayangkan skenario di mana objek yang lewat besar dan hanya digunakan olehRInterfaceHL
ctor untuk mendapatkan beberapa nilai, dan tidak menyimpan referensi untuk itu.return SingletonHolder.INSTANCE
akan bekerja dengan baik digetInstance
. Saya tidak berpikir ada kebutuhan untuk enkapsulasi di sini, karena kelas luar sudah tahu jeroan kelas dalam, mereka erat digabungkan: ia tahurloopHandler
perlu init sebelum memanggil. Juga konstruktor pribadi tidak memiliki efek, karena barang pribadi kelas dalam hanya tersedia untuk kelas luar.Alasan mengapa Anda tidak bisa memahami bagaimana mencapai apa yang Anda coba lakukan mungkin karena apa yang Anda coba lakukan tidak benar-benar masuk akal. Anda ingin menelepon
getInstance(x)
dengan argumen yang berbeda, tetapi selalu mengembalikan objek yang sama? Perilaku apa yang Anda inginkan saat menelepongetInstance(2)
dan kemudiangetInstance(5)
?Jika Anda ingin objek yang sama tetapi untuk nilai internal yang berbeda, yang merupakan satu-satunya cara itu masih tunggal, maka Anda tidak perlu peduli sama sekali dengan konstruktor; Anda cukup menetapkan nilai pada
getInstance()
pada jalan keluar objek. Tentu saja, Anda mengerti bahwa semua referensi Anda yang lain tentang singleton sekarang memiliki nilai internal yang berbeda.Jika Anda ingin
getInstance(2)
dangetInstance(5)
mengembalikan objek yang berbeda, di sisi lain, Anda tidak menggunakan pola Singleton, Anda menggunakan pola Factory.sumber
Dalam contoh Anda, Anda tidak menggunakan singleton. Perhatikan bahwa jika Anda melakukan hal berikut (dengan asumsi bahwa Singleton.getInstance sebenarnya statis):
Maka nilai-nilai obj2.x adalah 3, bukan 4. Jika Anda perlu melakukan ini, jadikan ini kelas biasa. Jika jumlah nilai kecil dan tetap, Anda dapat mempertimbangkan menggunakan
enum
. Jika Anda mengalami masalah dengan pembuatan objek berlebihan (yang biasanya tidak terjadi), maka Anda dapat mempertimbangkan nilai caching (dan memeriksa sumber atau mendapatkan bantuan dengan itu, karena jelas cara membuat cache tanpa bahaya kebocoran memori).Anda juga mungkin ingin membaca artikel ini karena lajang dapat dengan mudah digunakan secara berlebihan.
sumber
Alasan lain mengapa Singletons anti-pola adalah bahwa jika ditulis sesuai rekomendasi, dengan konstruktor pribadi, mereka sangat sulit untuk subkelas dan terkonfigurasi untuk digunakan dalam unit test tertentu. Akan diperlukan dalam memelihara kode warisan, misalnya.
sumber
Jika Anda ingin membuat kelas Singleton yang berfungsi sebagai Konteks, cara yang baik adalah memiliki file konfigurasi dan membaca parameter dari file di dalam instance ().
Jika parameter yang memberi makan kelas Singleton didapat secara dinamis selama menjalankan program Anda, cukup gunakan HashMap statis yang menyimpan instance berbeda di kelas Singleton Anda untuk memastikan bahwa untuk setiap parameter, hanya satu instance dibuat.
sumber
Ini bukan singleton, tetapi mungkin sesuatu yang bisa memperbaiki masalah Anda.
sumber
Jika kita menganggap masalah sebagai "cara membuat singleton dengan status", maka tidak perlu meneruskan status sebagai parameter konstruktor. Saya setuju dengan posting yang menginisialisasi negara atau menggunakan metode set setelah mendapatkan instance singleton.
Pertanyaan lain adalah: apakah baik memiliki singleton dengan negara?
sumber
Tidak bisakah kita melakukan sesuatu seperti ini:
sumber
Terlepas dari apa yang beberapa orang mungkin nyatakan, ini adalah singleton dengan parameter dalam konstruktor
Pola tunggal mengatakan:
yang dihormati dengan contoh ini.
Mengapa tidak langsung mengatur properti? Ini kasus buku teks untuk menunjukkan bagaimana kita bisa mendapatkan singleton yang memiliki konstruktor dengan parameter tetapi bisa berguna dalam beberapa situasi. Misalnya dalam kasus warisan memaksa singleton untuk mengatur beberapa properti superclass.
sumber
Saya takut memposting ini sebagai jawaban, tapi saya tidak mengerti mengapa tidak ada yang berpikir tentang ini, mungkin jawaban ini juga sudah diberikan, saya hanya tidak memahaminya.
Karena
getInstance()
mengembalikan Instance yang sama setiap kali, saya pikir ini bisa berhasil. Jika ini salah pada banyak hal yang akan saya hapus, saya hanya tertarik dengan topik ini.sumber
Saya pikir ini adalah masalah umum. Memisahkan "inisialisasi" singleton dari "get" singleton mungkin berhasil (contoh ini menggunakan variasi penguncian ganda diperiksa).
sumber
Singleton, tentu saja, merupakan "anti-pola" (dengan asumsi definisi statis dengan keadaan variabel).
Jika Anda ingin satu set objek nilai tetap yang tidak dapat diubah, maka enum adalah cara untuk melakukannya. Untuk sekumpulan nilai besar, mungkin ujung terbuka, Anda bisa menggunakan Repositori dari beberapa bentuk - biasanya berdasarkan
Map
implementasi. Tentu saja, ketika Anda berurusan dengan statika, berhati-hatilah dengan threading (baik menyinkronkan cukup luas atau menggunakanConcurrentMap
baik memeriksa bahwa utas lain belum mengalahkan Anda atau menggunakan beberapa bentuk futures).sumber
Lajang umumnya dianggap anti-pola dan tidak boleh digunakan. Mereka tidak membuat kode mudah untuk diuji.
Lajang dengan argumen tidak masuk akal - apa yang akan terjadi jika Anda menulis:
Singleton Anda juga tidak aman utas karena beberapa utas dapat membuat panggilan simultan untuk
getInstance
menghasilkan lebih dari satu instance yang dibuat (mungkin dengan nilai yang berbeda darix
).sumber