Sunting: Dari pertanyaan lain saya memberikan jawaban yang memiliki tautan ke banyak pertanyaan / jawaban tentang lajang: Info lebih lanjut tentang lajang di sini:
Jadi saya telah membaca thread Singletons: desain yang bagus atau tongkat penyangga?
Dan argumennya masih mengamuk.
Saya melihat Singletons sebagai Pola Desain (baik dan buruk).
Masalah dengan Singleton bukanlah Pola melainkan pengguna (maaf semuanya). Semua orang dan ayah mereka berpikir mereka bisa menerapkannya dengan benar (dan dari banyak wawancara yang saya lakukan, kebanyakan orang tidak bisa). Juga karena semua orang berpikir mereka dapat mengimplementasikan Singleton yang benar mereka menyalahgunakan Pola dan menggunakannya dalam situasi yang tidak tepat (mengganti variabel global dengan Singletons!).
Jadi pertanyaan utama yang perlu dijawab adalah:
- Kapan sebaiknya Anda menggunakan Singleton
- Bagaimana Anda menerapkan Singleton dengan benar
Harapan saya untuk artikel ini adalah bahwa kita dapat mengumpulkan bersama di satu tempat (daripada harus mencari google dan mencari beberapa situs) sumber otoritatif kapan (dan kemudian bagaimana) menggunakan Singleton dengan benar. Yang juga sesuai adalah daftar Anti-Penggunaan dan implementasi umum yang buruk yang menjelaskan mengapa mereka gagal bekerja dan untuk implementasi yang baik, kelemahan mereka.
Jadi
siapkan bola: Saya akan mengangkat tangan dan mengatakan inilah yang saya gunakan tetapi mungkin ada masalah.
Saya suka "Scott Myers" menangani subjek dalam buku-bukunya "Effective C ++"
Situasi Baik untuk menggunakan Lajang (tidak banyak):
- Kerangka kerja logging
- Kolam daur ulang benang
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
BAIK. Mari kita mendapatkan beberapa kritik dan implementasi lainnya bersama-sama.
:-)
sumber
Jawaban:
Kalian semua salah. Baca pertanyaannya. Menjawab:
Gunakan Singleton jika:
Jangan menggunakan Singleton jika:
Cara membuat singleton terbaik:
sumber
Lajang memberi Anda kemampuan untuk menggabungkan dua sifat buruk dalam satu kelas. Itu salah dalam banyak hal.
Singleton memberi Anda:
Nomor satu sangat mudah. Global pada umumnya buruk. Kita seharusnya tidak pernah membuat objek dapat diakses secara global kecuali kita benar-benar - membutuhkannya.
Nomor dua mungkin terdengar masuk akal, tapi mari kita pikirkan. Kapan terakhir kali Anda ** secara tidak sengaja * membuat objek baru alih-alih merujuk yang sudah ada? Karena ini ditandai C ++, mari gunakan contoh dari bahasa itu. Apakah Anda sering tidak sengaja menulis
Ketika Anda berniat untuk menulis
Tentu saja tidak. Kami tidak memerlukan perlindungan terhadap kesalahan ini, karena kesalahan semacam itu tidak terjadi. Jika ya, respons yang benar adalah pulang dan tidur selama 12-20 jam dan berharap Anda merasa lebih baik.
Jika hanya satu objek yang dibutuhkan, cukup buat satu instance. Jika satu objek harus dapat diakses secara global, jadikan itu objek global. Tapi itu tidak berarti tidak mungkin membuat contoh lain darinya.
Batasan "hanya satu contoh mungkin" tidak benar-benar melindungi kami dari kemungkinan bug. Tapi itu tidak membuat kode kita sangat sulit untuk refactor dan memelihara. Karena cukup sering kita temukan kemudian bahwa kita membutuhkan lebih dari satu contoh. Kami melakukan memiliki lebih dari satu database, kita jangan telah lebih dari satu objek konfigurasi, kita ingin beberapa penebang. Unit test kami mungkin ingin dapat membuat dan menciptakan kembali objek-objek ini setiap pengujian, untuk mengambil contoh umum.
Jadi singleton harus digunakan jika dan hanya jika, kita membutuhkan kedua sifat yang ditawarkannya: Jika kita membutuhkan akses global (yang jarang terjadi, karena global pada umumnya berkecil hati) dan kita perlu mencegah siapa pun membuat lebih dari satu instance dari sebuah kelas (yang bagi saya terdengar seperti masalah desain). Satu-satunya alasan saya dapat melihat untuk ini adalah jika membuat dua contoh akan merusak keadaan aplikasi kita - mungkin karena kelas berisi sejumlah anggota statis atau kekonyolan serupa. Dalam hal ini jawaban yang jelas adalah untuk memperbaiki kelas itu. Seharusnya tidak tergantung pada menjadi satu-satunya contoh.
Jika Anda membutuhkan akses global ke suatu objek, buat itu menjadi global, seperti
std::cout
. Tetapi jangan membatasi jumlah instance yang dapat dibuat.Jika Anda benar-benar, secara positif perlu membatasi jumlah instance kelas menjadi hanya satu, dan tidak ada cara membuat instance kedua dapat ditangani dengan aman, maka tegakkan itu. Tetapi jangan membuatnya dapat diakses secara global juga.
Jika Anda memang membutuhkan kedua sifat tersebut, maka 1) menjadikannya singleton, dan 2) beri tahu saya untuk apa Anda membutuhkannya, karena saya kesulitan membayangkan kasus seperti itu.
sumber
Masalah dengan lajang bukanlah implementasinya. Mereka mengacaukan dua konsep yang berbeda, yang keduanya tidak diinginkan.
1) Lajang menyediakan mekanisme akses global ke suatu objek. Meskipun mereka mungkin sedikit lebih aman di threads atau sedikit lebih dapat diandalkan dalam bahasa tanpa urutan inisialisasi yang didefinisikan dengan baik, penggunaan ini masih setara dengan moral variabel global. Ini adalah variabel global yang didandani dengan beberapa sintaks yang aneh (foo :: get_instance (), bukan g_foo, katakanlah), tetapi ia melayani tujuan yang sama persis (satu objek yang dapat diakses di seluruh program) dan memiliki kelemahan yang sama persis.
2) Lajang mencegah beberapa instance kelas. Jarang, IME, bahwa fitur semacam ini harus dimasukkan ke dalam kelas. Ini biasanya hal yang jauh lebih kontekstual; banyak hal yang dianggap sebagai satu-dan-hanya-satu benar-benar terjadi pada satu-satunya. IMO solusi yang lebih tepat adalah dengan hanya membuat satu instance - sampai Anda menyadari bahwa Anda membutuhkan lebih dari satu instance.
sumber
Satu hal dengan pola: jangan digeneralisasi . Mereka memiliki semua kasus ketika mereka berguna, dan ketika mereka gagal.
Singleton dapat menjadi jahat ketika Anda harus menguji kode. Anda biasanya terjebak dengan satu instance kelas, dan dapat memilih antara membuka pintu di konstruktor atau beberapa metode untuk mengatur ulang keadaan dan sebagainya.
Masalah lainnya adalah bahwa Singleton sebenarnya tidak lebih dari sebuah variabel global yang menyamar. Ketika Anda memiliki terlalu banyak keadaan global yang dibagi atas program Anda, banyak hal cenderung kembali, kita semua tahu itu.
Ini dapat membuat pelacakan ketergantungan menjadi lebih sulit. Ketika semuanya tergantung pada Singleton Anda, lebih sulit untuk mengubahnya, dipecah menjadi dua, dll. Anda umumnya terjebak dengannya. Ini juga menghambat fleksibilitas. Selidiki beberapa kerangka kerja Injeksi Ketergantungan untuk mencoba mengatasi masalah ini.
sumber
Lajang pada dasarnya membiarkan Anda memiliki keadaan global yang kompleks dalam bahasa yang sebaliknya membuat sulit atau tidak mungkin memiliki variabel global yang kompleks.
Java khususnya menggunakan lajang sebagai pengganti variabel global, karena semuanya harus terkandung dalam kelas. Yang paling dekat dengan variabel global adalah variabel statis publik, yang dapat digunakan seolah-olah merupakan variabel global
import static
C ++ memang memiliki variabel global, tetapi urutan konstruktor variabel kelas global dipanggil tidak ditentukan. Dengan demikian, singleton memungkinkan Anda menunda pembuatan variabel global hingga pertama kali variabel itu diperlukan.
Bahasa seperti Python dan Ruby menggunakan singleton sangat sedikit karena Anda dapat menggunakan variabel global dalam sebuah modul sebagai gantinya.
Jadi kapan baik / buruk menggunakan singleton? Cukup tepat kapan akan baik / buruk menggunakan variabel global.
sumber
Desain C ++ Modern oleh Alexandrescu memiliki singleton generik yang aman untuk diwariskan.
Untuk nilai 2p saya, saya pikir sangat penting untuk menentukan masa hidup untuk lajang Anda (ketika benar-benar diperlukan untuk menggunakannya). Saya biasanya tidak membiarkan
get()
fungsi statis instantiate apa pun, dan meninggalkan set-up dan penghancuran ke beberapa bagian khusus aplikasi utama. Ini membantu menyoroti ketergantungan antara lajang - tetapi, seperti ditekankan di atas, yang terbaik adalah menghindarinya jika memungkinkan.sumber
Ada satu masalah yang belum pernah saya lihat disebutkan, sesuatu yang saya temui di pekerjaan sebelumnya. Kami memiliki C ++ lajang yang dibagikan di antara DLL, dan mekanisme yang biasa untuk memastikan satu instance kelas tidak bekerja. Masalahnya adalah bahwa setiap DLL mendapatkan set variabel statis sendiri, bersama dengan EXE. Jika fungsi get_instance Anda sebaris atau bagian dari perpustakaan statis, setiap DLL akan berakhir dengan salinan "singleton" sendiri.
Solusinya adalah memastikan kode singleton hanya didefinisikan dalam satu DLL atau EXE, atau membuat manajer singleton dengan properti-properti itu untuk membagi contoh.
sumber
Contoh pertama tidak aman utas - jika dua utas memanggil getInstance pada saat yang sama, statis itu akan menjadi PITA. Beberapa bentuk mutex akan membantu.
sumber
Seperti yang telah dicatat oleh orang lain, kerugian besar bagi para lajang mencakup ketidakmampuan untuk memperpanjang mereka, dan kehilangan kekuatan untuk membuat lebih dari satu contoh, misalnya untuk tujuan pengujian.
Beberapa aspek yang berguna dari lajang:
Namun, Anda tidak harus menggunakan singleton untuk mendapatkan manfaat ini. Anda dapat menulis objek normal yang berfungsi, dan kemudian meminta orang mengaksesnya melalui pabrik (objek terpisah). Pabrik hanya bisa khawatir tentang instantiating satu, dan menggunakannya kembali, dll, jika perlu. Juga, jika Anda memprogram ke antarmuka daripada kelas konkret, pabrik dapat menggunakan strategi, yaitu Anda dapat beralih masuk dan keluar berbagai implementasi antarmuka.
Akhirnya, sebuah pabrik cocok untuk teknologi injeksi ketergantungan seperti Spring dll.
sumber
Lajang berguna ketika Anda memiliki banyak kode yang dijalankan ketika Anda menginisialisasi dan objek. Misalnya, ketika Anda menggunakan iBatis ketika Anda mengatur objek kegigihan, ia harus membaca semua konfigurasi, mengurai peta, pastikan semuanya benar, dll. Sebelum membuka kode Anda.
Jika Anda melakukan ini setiap waktu, kinerja akan jauh menurun. Menggunakannya dalam singleton, Anda menerima pukulan itu sekali dan kemudian semua panggilan berikutnya tidak harus melakukannya.
sumber
Kejatuhan Singletons yang sebenarnya adalah mereka menghancurkan warisan. Anda tidak dapat menurunkan kelas baru untuk memberi Anda fungsionalitas tambahan kecuali Anda memiliki akses ke kode tempat Singleton direferensikan. Jadi, di luar kenyataan Singleton akan membuat kode Anda digabungkan dengan erat (diperbaiki oleh Pola Strategi ... alias Dependency Injection) juga akan mencegah Anda menutup bagian kode dari revisi dari (shared library).
Jadi, bahkan contoh penebang atau kumpulan utas tidak valid dan harus diganti oleh Strategi.
sumber
Kebanyakan orang menggunakan lajang ketika mereka berusaha membuat diri mereka merasa senang menggunakan variabel global. Ada kegunaan yang sah, tetapi sebagian besar waktu ketika orang menggunakannya, fakta bahwa hanya ada satu contoh hanyalah fakta sepele dibandingkan dengan fakta bahwa itu dapat diakses secara global.
sumber
Karena singleton hanya memungkinkan satu instance dibuat, ia secara efektif mengontrol replikasi instance. misalnya Anda tidak akan membutuhkan beberapa contoh pencarian - misalnya peta pencarian morse, sehingga membungkusnya dalam kelas tunggal adalah tepat. Dan hanya karena Anda memiliki satu instance dari kelas tidak berarti Anda juga terbatas pada jumlah referensi ke instance itu. Anda dapat mengantri panggilan (untuk menghindari masalah threading) ke instance dan efek perubahan yang diperlukan. Ya, bentuk umum dari singleton adalah publik global, Anda tentu dapat memodifikasi desain untuk membuat singleton terbatas akses lebih. Saya belum pernah lelah ini sebelumnya, tetapi saya yakin itu mungkin. Dan bagi semua yang berkomentar mengatakan pola tunggal benar-benar jahat, Anda harus tahu ini:
sumber
Tetapi ketika saya membutuhkan sesuatu seperti Singleton, saya sering berakhir menggunakan Penghitung Schwarz untuk membuat instantiate.
sumber
Di bawah ini adalah pendekatan yang lebih baik untuk menerapkan pola single safe thread dengan deallocating memori di destructor itu sendiri. Tapi saya pikir destructor harus menjadi opsional karena instance singleton akan secara otomatis dihancurkan ketika program berakhir:
Mengenai situasi di mana kita perlu menggunakan kelas singleton dapat- Jika kita ingin mempertahankan keadaan instance selama pelaksanaan program Jika kita terlibat dalam penulisan ke dalam log eksekusi aplikasi di mana hanya satu instance dari file perlu digunakan .... dan seterusnya. Akan sangat bagus jika ada yang bisa menyarankan optimasi dalam kode saya di atas.
sumber
Saya menggunakan Lajang sebagai tes wawancara.
Ketika saya meminta pengembang untuk memberi nama beberapa pola desain, jika yang mereka bisa sebut adalah Singleton, mereka tidak dipekerjakan.
sumber
Anti-Penggunaan:
Salah satu masalah utama dengan penggunaan tunggal berlebihan adalah bahwa pola mencegah ekstensi mudah dan bertukar implementasi alternatif. Nama kelas adalah kode keras di mana pun singleton digunakan.
sumber
Saya pikir ini adalah versi paling kuat untuk C #:
Berikut adalah versi .NET-dioptimalkan :
Anda dapat menemukan pola ini di dotfactory.com .
sumber
Pola tunggal Meyers bekerja cukup baik sebagian besar waktu, dan pada kesempatan itu tidak perlu membayar untuk mencari sesuatu yang lebih baik. Selama konstruktor tidak akan pernah membuang dan tidak ada ketergantungan antara lajang.
Singleton adalah implementasi untuk objek yang dapat diakses secara global (GAO mulai sekarang) meskipun tidak semua GAO adalah lajang.
Penebang sendiri seharusnya bukan orang lajang, tetapi sarana untuk log idealnya dapat diakses secara global, untuk memisahkan di mana pesan log dihasilkan dari tempat atau bagaimana ia dicatat.
Evaluasi malas-malas / malas adalah konsep yang berbeda dan biasanya singleton menerapkannya juga. Itu datang dengan banyak masalah sendiri, khususnya keamanan benang dan masalah jika gagal dengan pengecualian sehingga apa yang tampak seperti ide yang baik pada saat itu ternyata tidak terlalu bagus. (Agak seperti implementasi SAP dalam string).
Dengan mengingat hal itu, GOA dapat diinisialisasi seperti ini:
Itu tidak perlu dilakukan dengan kasar seperti itu, dan jelas di perpustakaan dimuat yang berisi objek Anda mungkin ingin beberapa mekanisme lain untuk mengatur masa hidup mereka. (Tempatkan mereka di objek yang Anda dapatkan saat memuat perpustakaan).
Adapun ketika saya menggunakan lajang? Saya menggunakan mereka untuk 2 hal - Tabel tunggal yang menunjukkan perpustakaan apa yang telah dimuat dengan dlopen - Penangan pesan yang dapat dilanggan oleh para penebang dan Anda dapat mengirim pesan. Diperlukan khusus untuk penangan sinyal.
sumber
Saya masih belum mengerti mengapa seorang lajang harus mendunia.
Saya akan menghasilkan singleton di mana saya menyembunyikan database di dalam kelas sebagai variabel statis konstan pribadi dan membuat fungsi kelas yang memanfaatkan database tanpa pernah memaparkan database kepada pengguna.
Saya tidak melihat mengapa fungsi ini buruk.
sumber
Saya menemukan mereka berguna ketika saya memiliki kelas yang merangkum banyak memori. Sebagai contoh dalam permainan baru-baru ini saya telah bekerja pada Saya memiliki kelas peta pengaruh yang berisi koleksi array yang sangat besar dari memori yang berdekatan. Saya ingin semua dialokasikan saat startup, semua dibebaskan saat shutdown dan saya pasti hanya menginginkan satu salinannya. Saya juga harus mengaksesnya dari banyak tempat. Saya menemukan pola singleton sangat berguna dalam kasus ini.
Saya yakin ada solusi lain tetapi saya menemukan ini sangat berguna dan mudah diimplementasikan.
sumber
Jika Anda adalah orang yang menciptakan singleton dan yang menggunakannya, jangan menjadikannya sebagai singleton (tidak masuk akal karena Anda dapat mengontrol singularitas objek tanpa membuatnya singleton) tetapi masuk akal ketika Anda seorang pengembang sebuah perpustakaan dan Anda ingin menyediakan hanya satu objek untuk pengguna Anda (dalam hal ini Anda yang menciptakan singleton, tetapi Anda bukan pengguna).
Lajang adalah objek jadi gunakan itu sebagai objek, banyak orang mengakses lajang secara langsung melalui memanggil metode yang mengembalikannya, tetapi ini berbahaya karena Anda membuat kode Anda tahu bahwa objek itu singleton, saya lebih suka menggunakan lajang sebagai objek, saya meneruskannya melalui konstruktor dan saya menggunakannya sebagai objek biasa, dengan cara itu, kode Anda tidak tahu apakah objek ini lajang atau tidak dan itu membuat dependensi lebih jelas dan ini membantu sedikit untuk refactoring ...
sumber
Di aplikasi desktop (saya tahu, hanya kita dinosaurus yang menulis ini lagi!) Mereka sangat penting untuk mendapatkan pengaturan aplikasi global yang relatif tidak berubah - bahasa pengguna, jalur untuk membantu file, preferensi pengguna dll. Jika tidak maka harus mempropogasi ke setiap kelas dan setiap dialog .
Sunting - tentu saja ini harus hanya baca!
sumber
Implementasi lain
sumber
Instance()
harus mengembalikan pointer, bukan referensi. Di dalam Anda.cpp
berkas, menginisialisasi contoh untuk nol:Singleton* Singleton::instance_ = nullptr;
. DanInstance()
harus dilaksanakan sebagai:if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.