Singleton: Bagaimana seharusnya digunakan


291

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.
:-)


36
Bagaimana jika nanti Anda memutuskan ingin beberapa penebang? Atau beberapa kumpulan utas? Jika Anda hanya ingin satu logger, maka buat saja satu instance dan buat global. Lajang hanya baik jika Anda benar-benar MEMBUTUHKAN hanya ada satu dan itu MEMBUTUHKAN untuk menjadi global, IMHO.

3
Siapa bilang framework hanya bisa memiliki 1 instance logger. Satu singelton mewakili Framework. Framwork kemudian dapat memberi Anda penebang tertentu.
Martin York

Ya. Saya tidak akan menggunakan singeltong sebagai threadpool. Hanya membuang ide untuk memicu jawaban.
Martin York

@Dan Singleton yang mengimplementasikan pola strategi. Perilaku disarikan dari singleton. Singleton adalah titik masuk tunggal. Tidak punya dua penebang, punya satu logger yang bisa memutuskan cara login. Anda tidak bisa hanya menghasilkan satu log pada satu waktu, tidak perlu memiliki dua log.
Lee Louviere

5
Xaade: bagaimana jika Anda ingin login ke dua file? Atau ke database? Atau soket jaringan? Atau widget GUI? Intinya, jangan tambahkan pembatasan buatan - tidak perlu. Seberapa sering Anda pernah secara tidak sengaja membuat dua untuk loop, bukan hanya satu? Jika Anda hanya ingin satu logger maka buat hanya satu.

Jawaban:


182

Kalian semua salah. Baca pertanyaannya. Menjawab:

Gunakan Singleton jika:

  • Anda harus memiliki satu dan hanya satu objek dari jenis dalam sistem

Jangan menggunakan Singleton jika:

  • Anda ingin menghemat memori
  • Anda ingin mencoba sesuatu yang baru
  • Anda ingin memamerkan seberapa banyak yang Anda tahu
  • Karena semua orang melakukannya (Lihat programmer kultus kargo di wikipedia)
  • Di widget antarmuka pengguna
  • Seharusnya cache
  • Dalam string
  • Dalam Sesi
  • Saya bisa sepanjang hari

Cara membuat singleton terbaik:

  • Semakin kecil, semakin baik. Saya seorang minimalis
  • Pastikan benang aman
  • Pastikan itu tidak pernah nol
  • Pastikan itu dibuat hanya sekali
  • Malas atau inisialisasi sistem? Hingga kebutuhan Anda
  • Terkadang OS atau JVM membuat lajang untuk Anda (mis. Di Jawa setiap definisi kelas adalah lajang)
  • Berikan destruktor atau entah bagaimana mencari cara membuang sumber daya
  • Gunakan sedikit memori

14
Sebenarnya, saya pikir Anda juga tidak benar. Saya akan ulangi sebagai: "Jika Anda perlu memiliki satu dan hanya satu objek dari jenis dalam sistem DAN Anda perlu memiliki akses global untuk itu" Penekanan pada kebutuhan adalah milikku - jangan lakukan itu jika itu nyaman, hanya jika Anda HARUS memilikinya.

91
Anda salah juga. Jika Anda membutuhkan satu dan hanya satu objek, Anda membuat satu dan hanya satu. Jika tidak ada cara logis agar dua instance dapat diakomodasi tanpa merusak aplikasi yang tidak dapat dibatalkan, Anda harus mempertimbangkan untuk menjadikannya sebagai singleton. Dan kemudian ada aspek lain, akses global: Jika Anda tidak membutuhkan akses global ke instance, itu seharusnya bukan singleton.
jalf

4
Ditutup untuk modifikasi, terbuka untuk ekstensi. Masalahnya adalah Anda tidak dapat memperpanjang singleton menjadi duoton atau tripleton. Itu terjebak sebagai singleton.
Lee Louviere

2
@ enzom83: Huruf kapital-S Singleton termasuk kode untuk memastikan keunikannya. Jika Anda hanya ingin satu contoh, Anda dapat kehilangan kode itu dan cukup membuat satu contoh sendiri ... memberi Anda penghematan memori satu contoh, ditambah penghematan dari penghilangan kode penegakan melajang - yang juga berarti tidak mengorbankan kemampuan untuk membuat instance kedua jika kebutuhan Anda berubah.
cHao

4
"Jika Anda perlu memiliki satu dan hanya satu objek dari jenis dalam sistem" - "... dan tidak pernah ingin mengejek objek itu dalam unit test."
Cygon

72

Lajang memberi Anda kemampuan untuk menggabungkan dua sifat buruk dalam satu kelas. Itu salah dalam banyak hal.

Singleton memberi Anda:

  1. Akses global ke suatu objek, dan
  2. Jaminan bahwa tidak ada lebih dari satu objek jenis ini yang dapat dibuat

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

std::ostream os;
os << "hello world\n";

Ketika Anda berniat untuk menulis

std::cout << "hello world\n";

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.


3
atau Anda bisa menjadikannya global, dan dapatkan hanya satu kerugian dari seorang singleton. Dengan singleton, Anda secara bersamaan akan membatasi diri Anda pada satu instance dari kelas database itu. Kenapa melakukan itu? Atau Anda bisa melihat mengapa Anda memiliki begitu banyak dependensi sehingga daftar instantiation menjadi "sangat panjang". Apakah semua itu perlu? Haruskah beberapa dari mereka didelegasikan ke komponen lain? Mungkin beberapa dari mereka bisa dikemas bersama dalam sebuah struct sehingga kita bisa menyampaikannya sebagai satu argumen. Ada banyak solusi, semuanya lebih baik daripada lajang.
jalf

6
Ya, seorang singleton mungkin dibenarkan di sana. Tapi saya pikir Anda baru saja membuktikan maksud saya bahwa itu hanya perlu dalam kasus yang cukup eksotis. Sebagian besar perangkat lunak tidak berurusan dengan perangkat keras pembajak salju. Tapi saya masih belum yakin. Saya setuju bahwa dalam aplikasi Anda yang sebenarnya, Anda hanya menginginkan salah satunya. Tapi bagaimana dengan unit test Anda? Masing-masing dari mereka harus berjalan dalam isolasi, sehingga mereka idealnya harus membuat SpreaderController mereka sendiri - yang sulit dilakukan dengan singleton. Akhirnya, mengapa rekan kerja Anda menciptakan banyak contoh di tempat pertama? Apakah itu skenario realistis untuk dilindungi?
jalf

3
Dan satu hal yang Anda lewatkan adalah bahwa sementara dua contoh terakhir Anda dapat dibenarkan membenarkan batasan "hanya satu contoh", mereka tidak melakukan apa pun untuk membenarkan yang "dapat diakses secara global". Mengapa seluruh basis kode dapat mengakses unit administrasi sakelar telepon Anda? Intinya dalam singleton adalah memberi Anda berdua sifat. Jika Anda hanya membutuhkan satu atau yang lain, Anda tidak harus menggunakan singleton.
jalf

2
@ jalf - Tujuan saya hanya untuk memberi Anda contoh di mana Singleton berguna di alam, karena Anda tidak bisa membayangkan apa pun; Saya kira Anda tidak melihat berkali-kali untuk menerapkannya sebagai pekerjaan Anda saat ini. Saya beralih ke pemrograman bajak salju dari aplikasi bisnis hanya karena itu akan memungkinkan saya untuk menggunakan Singleton. :) j / k Saya setuju dengan premis Anda bahwa ada cara yang lebih baik untuk melakukan hal-hal ini, Anda telah memberi saya banyak hal untuk dipikirkan. Terima kasih untuk diskusi!
J. Polfer

2
Menggunakan "pola" singleton ( AHEM! ) Untuk mencegah orang-orang menanamkan lebih banyak contoh adalah hal yang bodoh untuk mencegah orang melakukan hal itu dengan tepat. Ketika saya memiliki variabel lokal foo1 dari tipe Foo di fungsi kecil saya dan hanya ingin satu di dalam fungsi, saya tidak khawatir bahwa seseorang akan membuat foo2 foo2 kedua yang mudah berubah dan menggunakannya daripada yang asli.
Thomas Eding

36

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.


6
Sepakat. Dua kesalahan dapat membuat benar menurut beberapa orang, di dunia nyata. Tetapi dalam pemrograman, mencampurkan dua ide buruk tidak menghasilkan yang baik.
jalf

27

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.


8
Tidak, singleton jauh lebih dari sekadar variabel global yang disamarkan. Itulah yang membuatnya sangat buruk. Ini menggabungkan ke-global-an (yang biasanya buruk) dengan konsep lain yang juga buruk (bahwa tidak membiarkan programmer membuat instance kelas jika dia memutuskan dia membutuhkan contoh) Mereka sering digunakan sebagai variabel global, ya. Dan kemudian mereka menyeret efek samping jahat lainnya juga, dan melumpuhkan basis kode.
jalf

7
Perlu juga dicatat bahwa lajang tidak harus memiliki aksesibilitas publik. Singleton bisa saja menjadi internal perpustakaan dan tidak pernah terpapar kepada pengguna. Jadi mereka tidak harus "global" dalam pengertian itu.
Steven Evers

1
+1 untuk menunjukkan seberapa sakit Singletons dalam pengujian.
DevSolar

1
@jalf Tidak mengizinkan seseorang untuk membuat lebih dari satu instance kelas bukanlah hal yang buruk. Jika benar-benar hanya ada satu instance dari instantiated class yang memberlakukan persyaratan. Jika seseorang kemudian memutuskan bahwa mereka perlu membuat contoh lain mereka harus memperbaikinya, karena itu seharusnya tidak menjadi singleton.
William

2
@ William: dan saya harus memiliki beberapa penebang dari waktu ke waktu. Anda tidak berdebat untuk seorang lajang, tetapi untuk orang tua biasa. Anda ingin tahu bahwa sebuah logger selalu tersedia. Untuk itulah tujuan global. Anda tidak perlu tahu bahwa tidak ada penebang lain yang dapat dibuatkan instantiasinya , yang mana diberlakukan oleh seorang lajang. (coba tulis unit test untuk logger Anda - itu jauh lebih mudah jika Anda dapat membuat dan menghancurkannya sesuai kebutuhan, dan itu tidak mungkin dengan singleton)
jalf

13

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 globalimport 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.


Kapan suatu variabel global "baik"? Terkadang mereka adalah solusi terbaik untuk suatu masalah, tetapi mereka tidak pernah "baik".
DevSolar

1
variabel global baik ketika digunakan di mana-mana, dan semuanya dapat memiliki akses ke sana. Implementasi mesin turing keadaan tunggal dapat memanfaatkan singleton.
Lee Louviere

Saya suka lapisan tipuan dalam jawaban ini: "ketika akan baik / buruk untuk menggunakan global". Baik DevSolar dan Lee Louviere mendapatkan nilai yang mereka setujui meskipun pada saat jawab tidak diketahui siapa yang akan berkomentar.
Praxeolitic

6

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.


6
  • Bagaimana Anda menerapkan Singleton dengan benar

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.


10
Yo dawg, saya mendengar Anda menyukai Singletons, jadi saya membuat Singleton untuk Singleton Anda, sehingga Anda dapat anti-pola sementara Anda anti-pola.
Eva

@ Eva, ya sesuatu seperti itu. Saya tidak membuat masalah, saya hanya harus membuatnya bekerja entah bagaimana.
Mark Ransom

5

Contoh pertama tidak aman utas - jika dua utas memanggil getInstance pada saat yang sama, statis itu akan menjadi PITA. Beberapa bentuk mutex akan membantu.


Yap yang tercantum dalam komentar di atas: * Batasan: Desain Berulir Tunggal * Lihat: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Untuk masalah yang terkait dengan penguncian dalam aplikasi multi-ulir
Martin York

Singleton klasik dengan hanya menggunakanInstance sebagai metode statis dan metode instan untuk operasi lain tidak akan pernah dapat membuat thread aman. (baik kecuali jika Anda membuatnya menjadi satu per thread-ton menggunakan penyimpanan lokal thread ...)
Tobi

bahkan di c ++ 11 atau lebih baru?
hg_git

5

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:

  1. instantiasi malas atau dimuka
  2. berguna untuk objek yang membutuhkan pengaturan dan / atau negara

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.


3

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.


The Prototype Pola melakukan hal ini juga, dan itu lebih fleksibel. Anda juga dapat menggunakannya ketika klien Anda akan membuat banyak instance dari kelas mahal Anda, tetapi hanya beberapa dari mereka yang benar-benar memiliki keadaan berbeda. Misalnya, tetronimos dalam Tetris.
Eva

3

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.


Penebang sendiri seharusnya tidak lajang. Seharusnya sistem pesan "siaran". Logger sendiri adalah pelanggan pesan broadcast.
CashCow

Thread-pool seharusnya juga bukan lajang. Pertanyaan umum adalah apakah Anda menginginkan lebih dari satu? Iya. Ketika saya terakhir menggunakannya, kami memiliki 3 thread-pool berbeda dalam satu aplikasi.
CashCow

3

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.


3

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:



2

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:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

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.


2
Itu jelas tidak lebih baik. 1: Anda tidak mendefinisikan semantik kepemilikan dengan menggunakan pointer. Anda tidak boleh menggunakan pointer dalam C ++ kecuali Anda siap untuk mengelolanya. 2: Penggunaan penguncian ganda diperiksa sudah kuno dan ada banyak cara modern yang lebih baik untuk melakukan ini. 3: Komentarmu tentang kehancuran itu naif. Reklamasi memori bukan titik penggunaan destruktor itu tentang pembersihan. Saran untuk versi yang lebih baik: Lihatlah pertanyaannya. Versi yang disajikan di sana sudah jauh lebih baik.
Martin York

1

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.


45
Aturan keras dan cepat tentang perekrutan akan membuat Anda kehilangan beragam karyawan potensial.
Karl

13
Ada beragam idiot. Itu tidak berarti mereka harus dipertimbangkan untuk direkrut. Jika seseorang dapat menyebutkan tidak ada pola desain sama sekali, saya pikir mereka akan lebih disukai daripada seseorang yang tahu singleton, dan tidak ada pola lain.
jalf

3
Sebagai buku catatan - tanggapan saya adalah lidah-di-pipi. Dalam proses wawancara saya yang sebenarnya, saya mencoba menilai apakah kita perlu mengajari seseorang di C ++, dan seberapa sulit itu akan terjadi. Beberapa kandidat favorit saya adalah orang-orang yang TIDAK tahu C ++ dalam dan luar, tapi saya bisa berbicara dengan mereka tentang hal itu.
Matt Cruikshank

4
Pemungutan suara Dari pengalaman pribadi saya - programmer mungkin tidak dapat menyebutkan pola lain selain Singleton, tetapi itu tidak berarti bahwa ia menggunakan Singletons. Secara pribadi, saya menggunakan lajang dalam kode saya SEBELUM saya pernah mendengar tentang mereka (saya menyebut mereka "global yang lebih pintar" - Saya tahu apa itu global). Ketika saya belajar tentang mereka, ketika saya belajar tentang pro dan kontra mereka - saya berhenti menggunakannya. Tiba-tiba, pengujian unit menjadi jauh lebih menarik bagi saya ketika saya berhenti ... Apakah itu membuat saya programmer yang lebih buruk? Pfff ...
Paulius

3
Saya juga downvoting untuk pertanyaan "nama beberapa pola desain" omong kosong. Mendesain adalah tentang memahami bagaimana menerapkan pola desain, tidak hanya bisa menghilangkan nama mereka. Oke, itu mungkin tidak menjamin downvote tetapi jawaban ini adalah troll-ish.
CashCow

0

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.


Diturunkan karena 2 alasan: 1. Singleton secara internal dapat menggunakan instance polimorfik (misalnya, global Logger menggunakan strategi polimorfik penargetan) 2. Ada dapat mengetik untuk nama singleton, sehingga kode secara faktual tergantung pada typedef.
toped gamedev

Saya akhirnya membangun versi singleton saya agar dapat diperluas menggunakan pola templat yang berulang berulang.
Zachary Kraus

0

Saya pikir ini adalah versi paling kuat untuk C #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Berikut adalah versi .NET-dioptimalkan :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Anda dapat menemukan pola ini di dotfactory.com .


3
Anda dapat menghapus bagian-bagian yang tidak secara khusus relavent ke Singletons untuk membuat kode lebih mudah dibaca.
Martin York

Juga, versi pertama Anda tidak aman karena kemungkinan membaca ulang / menulis ulang. Lihat stackoverflow.com/questions/9666/…
Thomas Danecker

5
Eh ... bahasa yang salah? Pertanyaannya cukup jelas ditandai C ++ .
DevSolar

0

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:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

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.


0

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.


Saya tidak mengerti mengapa Anda berpikir itu harus global.
Martin York

menurut utas ini, Semua orang mengatakan bahwa seorang singleton haruslah mendunia
Zachary Kraus

1
Tidak. Utas menunjukkan bahwa singelton memiliki status global. Bukan berarti itu adalah variabel global. Solusi yang Anda usulkan memiliki kondisi global. Solusi yang Anda usulkan juga menggunakan variabel global; anggota statis kelas adalah objek "Durasi Penyimpanan Statis" variabel global adalah objek "Durasi Penyimpanan Statis". Jadi keduanya pada dasarnya adalah hal yang sama dengan semantik / cakupan yang sedikit berbeda.
Martin York

Jadi variabel statis pribadi masih bersifat global karena "Durasi Penyimpanan Statis"?
Zachary Kraus

1
Catatan: Anda sengaja melewatkan bagian saya yang tidak disebutkan. Desain Anda menggunakan anggota statis "pribadi" tidak buruk dengan cara yang sama seperti singelton. Karena itu tidak memperkenalkan "negara bisa berubah global". Tapi itu juga bukan singleton. Singleton adalah kelas yang dirancang sehingga hanya satu instance objek yang bisa ada. Apa yang Anda sarankan adalah keadaan bersama tunggal untuk semua objek kelas. Konsep yang berbeda.
Martin York

0

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.


0

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 ...


-1

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!


Tapi ini menimbulkan pertanyaan; mengapa bahasa pengguna dan jalur ke file bantuan harus menjadi metode instance sama sekali ?
DrPizza

2
Kami punya global untuk itu. Tidak perlu membuat mereka lajang
jalf

Variabel global - lalu bagaimana Anda membuat serial mereka dari registry / databse? Kelas Gobal - lalu bagaimana Anda memastikan hanya ada satu dari mereka?
Martin Beckett

@ mgb: Anda membuat cerita bersambung dengan membaca nilai dari registri / database dan menyimpannya dalam variabel global (ini mungkin harus dilakukan di bagian atas fungsi utama Anda). Anda memastikan hanya ada satu objek kelas, dengan membuat hanya satu objek kelas ... sungguh ... apakah itu sulit untuk 'grep -rn "new \ + global_class_name".' ? Betulkah?
Paulius

7
@ mgb: Kenapa saya harus memastikan hanya ada satu? Saya hanya perlu tahu bahwa satu instance selalu mewakili pengaturan saat ini . tapi tidak ada alasan mengapa saya tidak boleh memiliki objek pengaturan lain di sekitar tempat itu. Mungkin satu untuk "pengaturan pengguna saat ini mendefinisikan, tetapi belum diterapkan", misalnya. Atau satu untuk "konfigurasi yang disimpan pengguna sebelumnya sehingga ia dapat kembali ke mereka nanti". Atau satu untuk setiap unit test Anda.
jalf

-1

Implementasi lain

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

3
Itu sangat mengerikan. Lihat: stackoverflow.com/questions/1008019/c-singleton-design-pattern/... untuk inisialisasi malas yang lebih baik dan yang lebih penting menjamin kehancuran deterministik.
Martin York

Jika Anda akan menggunakan pointer, Instance()harus mengembalikan pointer, bukan referensi. Di dalam Anda .cppberkas, menginisialisasi contoh untuk nol: Singleton* Singleton::instance_ = nullptr;. Dan Instance()harus dilaksanakan sebagai: if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.