Apa yang buruk tentang lajang? [Tutup]

1983

The pola tunggal adalah anggota disetor dari GOF 's pola buku , tetapi akhir-akhir ini tampaknya agak yatim piatu oleh dunia pengembang. Saya masih menggunakan banyak lajang, terutama untuk kelas pabrik , dan walaupun Anda harus sedikit berhati-hati tentang masalah multithreading (seperti kelas mana pun sebenarnya), saya gagal melihat mengapa mereka begitu mengerikan.

Stack Overflow terutama tampaknya menganggap bahwa semua orang setuju bahwa Singletons jahat. Mengapa?

Harap dukung jawaban Anda dengan " fakta, referensi, atau keahlian khusus "

m87
sumber
6
Saya harus mengatakan bahwa menggunakan desain tunggal telah membakar saya baru-baru ini karena saya telah mencoba untuk menyesuaikan kode. Ketika saya melakukannya di waktu senggang, saya hampir terlalu malas untuk menolaknya. Berita buruk untuk produktivitas.
Marcin
71
Ada banyak 'kontra' dalam jawaban, tetapi saya juga ingin melihat beberapa contoh bagus kapan polanya bagus, berbeda dengan yang buruk ...
DGM
50
Saya menulis posting blog tentang masalah ini beberapa bulan yang lalu: jalf.dk/blog/2010/03/... - dan izinkan saya mengatakannya langsung. Saya pribadi tidak bisa memikirkan satu situasi di mana seorang wanita lajang adalah solusi yang tepat. Itu tidak berarti situasi seperti itu tidak ada, tetapi ... menyebut mereka langka adalah pernyataan yang meremehkan.
Jalf
8
@ AdSmithSmith bukan berarti Anda harus melakukannya, tetapi itu berarti Anda dapat mengaksesnya seperti itu. Dan jika Anda tidak bermaksud mengaksesnya seperti itu, maka ada sedikit alasan untuk menjadikannya singleton. Jadi argumen Anda secara efektif "tidak ada salahnya membuat singleton jika kita tidak memperlakukannya sebagai singleton. Ya, bagus. Mobil saya juga tidak mencemari jika saya tidak mengendarainya. Tapi kemudian lebih mudah untuk hanya tidak mendapatkan mobil di tempat pertama;;) (pengungkapan penuh: Saya tidak benar-benar punya mobil)
jalf
35
Bagian terburuk dari keseluruhan topik ini adalah bahwa orang yang membenci lajang jarang memberikan saran konkret untuk apa yang harus digunakan. Tautan ke artikel jurnal dan blog yang diterbitkan sendiri di seluruh artikel SO ini, misalnya, terus dan terus tentang mengapa tidak menggunakan lajang (dan mereka semua alasan yang sangat baik), tetapi mereka sangat ramping pada penggantian. Meskipun begitu, banyak handwaving. Kita yang mencoba mengajari programmer baru mengapa tidak menggunakan lajang tidak memiliki banyak contoh tandingan pihak ketiga yang baik, hanya contoh yang dibuat-buat. Ini melelahkan.
Ti Strga

Jawaban:

1294

Parafrase dari Brian Button:

  1. Mereka umumnya digunakan sebagai contoh global, mengapa begitu buruk? Karena Anda menyembunyikan dependensi aplikasi Anda dalam kode Anda, alih-alih mengekspos mereka melalui antarmuka. Membuat sesuatu yang global untuk menghindari menyebarkannya adalah bau kode .

  2. Mereka melanggar prinsip tanggung jawab tunggal : berdasarkan fakta bahwa mereka mengendalikan penciptaan dan siklus hidup mereka sendiri.

  3. Mereka inheren menyebabkan kode yang akan erat digabungkan . Ini membuat berpura-pura mereka dalam ujian agak sulit dalam banyak kasus.

  4. Mereka membawa keadaan sekitar selama aplikasi seumur hidup. Hit lain untuk pengujian karena Anda bisa berakhir dengan situasi di mana tes harus dipesan yang merupakan tidak besar tidak untuk tes unit. Mengapa? Karena setiap unit tes harus independen dari yang lain.

Jim Burger
sumber
324
Saya tidak setuju dengan Anda. Karena komentar hanya diperbolehkan 600 karakter, saya telah menulis Posting Blog untuk mengomentari ini, silakan lihat tautan di bawah ini. jorudolph.wordpress.com/2009/11/22/singleton-considerations
Johannes Rudolph
61
Pada poin 1 dan 4, saya pikir lajang berguna, dan bahkan hampir sempurna untuk caching data (terutama dari DB). Peningkatan kinerja jauh melampaui kompleksitas yang terlibat dalam pemodelan tes unit.
Dai Bok
56
@Dai Bok: "caching data (terutama dari DB)" gunakan pola proxy untuk melakukan ini ...
paxos1977
55
Wow, respons yang bagus. Saya mungkin menggunakan kata-kata yang terlalu agresif di sini, jadi harap diingat saya menjawab pertanyaan yang diarahkan secara negatif. Jawaban saya dimaksudkan untuk menjadi daftar singkat 'pola anti' yang terjadi dari penggunaan Singleton yang buruk. Pengungkapan penuh; Saya juga menggunakan lajang dari waktu ke waktu. Ada lebih banyak pertanyaan yang diajukan secara netral pada SO yang akan membuat forum yang sangat baik ketika Singletons dapat dianggap sebagai ide yang bagus. Misalnya, stackoverflow.com/questions/228164/…
Jim Burger
21
Mereka tidak begitu bagus untuk caching di lingkungan multithreaded. Anda dapat dengan mudah mengalahkan cache dengan banyak utas memperebutkan sumber daya yang terbatas. [dikelola oleh orang Singapura]
monksy
449

Lajang memecahkan satu (dan hanya satu) masalah.

Kontensi Sumber Daya.

Jika Anda memiliki beberapa sumber daya

( 1 ) hanya dapat memiliki satu instance, dan

( 2 ) Anda perlu mengelola satu instance itu,

kamu butuh seorang singleton .

Tidak banyak contoh. File log adalah yang besar. Anda tidak ingin hanya meninggalkan satu file log. Anda ingin menyiram, menyinkronkan dan menutupnya dengan benar. Ini adalah contoh dari satu sumber daya bersama yang harus dikelola.

Jarang sekali Anda membutuhkan singleton. Alasan mereka buruk adalah karena mereka merasa seperti global dan mereka adalah anggota penuh dari buku Pola Desain GoF .

Ketika Anda berpikir Anda membutuhkan global, Anda mungkin membuat kesalahan desain yang mengerikan.

S.Lott
sumber
43
Perangkat keras juga contohnya kan? Sistem tertanam memiliki banyak perangkat keras yang mungkin menggunakan lajang - atau mungkin yang besar? <grin>
Jeff
170
Setuju. Ada banyak tekad "praktik ini buruk" berbicara di sekitar tanpa pengakuan bahwa praktik itu mungkin ada tempatnya. Seringkali, praktiknya hanya "buruk" karena sering disalahgunakan. Tidak ada yang salah dengan pola Singleton selama itu diterapkan dengan tepat.
Damovisa
53
Sebenarnya, lajang berdasarkan prinsip sangat jarang (dan antrian printer jelas bukan satu, dan juga bukan file log - lihat log4j). Lebih sering daripada tidak, perangkat keras adalah singleton oleh kebetulan daripada oleh prinsip. Semua perangkat keras yang terhubung ke PC adalah yang terbaik secara kebetulan (pikirkan beberapa monitor, mouse, printer, kartu suara). Bahkan detektor partikel $ 500 juta adalah singleton oleh kebetulan dan kendala anggaran - yang tidak berlaku dalam perangkat lunak, dengan demikian: tidak ada singleton. Dalam telekomunikasi, baik nomor telepon maupun telepon fisik adalah lajang (pikirkan ISDN, pusat panggilan).
digitalarbeiter
23
Perangkat keras mungkin hanya memiliki satu contoh dari berbagai sumber daya, tetapi perangkat keras bukan perangkat lunak. Tidak ada alasan bahwa port serial tunggal pada papan pengembangan harus dimodelkan sebagai Singleton. Memang, memodelkannya hanya akan membuat lebih sulit untuk port ke papan baru yang memiliki dua port!
dash-tom-bang
19
Jadi Anda akan menulis dua kelas yang identik, menduplikasi banyak kode, hanya agar Anda dapat memiliki dua lajang yang mewakili dua port? Bagaimana kalau hanya menggunakan dua objek SerialPort global? Bagaimana kalau tidak memodelkan perangkat keras sebagai kelas sama sekali? Dan mengapa Anda menggunakan lajang, yang juga menyiratkan akses global? Apakah Anda ingin setiap fungsi dalam kode Anda dapat mengakses port serial?
jalf
352

Beberapa penyandi pengkodean memandangnya hanya sebagai global yang dimuliakan. Dengan cara yang sama bahwa banyak orang membenci pernyataan goto ada orang lain yang membenci gagasan pernah menggunakan global . Saya telah melihat beberapa pengembang berusaha keras untuk menghindari global karena mereka menganggap menggunakannya sebagai pengakuan kegagalan. Aneh tapi Nyata.

Dalam praktiknya pola Singleton hanyalah teknik pemrograman yang merupakan bagian yang berguna dari perangkat konsep Anda. Dari waktu ke waktu Anda mungkin menemukan itu adalah solusi yang ideal dan gunakan itu. Tetapi menggunakannya hanya agar Anda dapat membanggakan tentang menggunakan pola desain sama bodohnya dengan menolak untuk menggunakannya karena itu hanya global .

Phil Wright
sumber
17
Kegagalan yang saya lihat di Singleton adalah bahwa orang-orang menggunakannya bukan global karena mereka entah bagaimana "lebih baik." Masalahnya adalah (seperti yang saya lihat), bahwa hal-hal yang dibawa Singleton ke meja dalam kasus-kasus ini tidak relevan. (Construct-on-first-use sepele untuk diterapkan dalam non-tunggal, misalnya, bahkan jika itu tidak benar-benar menggunakan konstruktor untuk melakukannya.)
dash-tom-bang
21
Alasan "kita" memandang rendah mereka adalah karena "kita" sangat sering melihat mereka menggunakan kesalahan, dan terlalu sering. "Kami" tahu bahwa mereka memiliki tempat tujuan.
Bjarke Freund-Hansen
5
@Phil, Anda menyatakan "dari waktu ke waktu Anda mungkin menemukan itu adalah solusi ideal dan karenanya gunakan". Ok, jadi tepatnya di mana kasus yang akan kita menemukan tunggal berguna?
Pacerier
22
@Pacerier, setiap kali semua kondisi berikut ini berlaku: (1) Anda hanya perlu satu dari sesuatu, (2) Anda harus menyampaikan satu hal sebagai argumen dalam sejumlah besar pemanggilan metode, (3) Anda bersedia menerima kemungkinan harus refactor suatu hari nanti dengan imbalan pengurangan segera dalam ukuran dan kompleksitas kode Anda dengan tidak melewatkan hal-hal sialan di mana-mana.
antinome
18
Saya tidak merasa bahwa ini menjawab apa pun, itu hanya mengatakan bahwa 'kadang-kadang mungkin cocok, di lain waktu mungkin tidak'. OK, tapi mengapa, dan kapan? Apa yang membuat jawaban ini lebih dari sekadar argumen moderasi ?
Guildenstern
219

Misko Hevery, dari Google, memiliki beberapa artikel menarik tentang topik ini ...

Lajang adalah Pembohong Patologis memiliki contoh pengujian unit yang menggambarkan bagaimana lajang dapat mempersulit untuk mengetahui rantai ketergantungan dan memulai atau menguji suatu aplikasi. Ini adalah contoh pelecehan yang cukup ekstrem, tetapi poin yang ia buat masih valid:

Lajang tidak lebih dari negara global. Status global membuatnya sehingga objek Anda dapat secara diam-diam mendapatkan hal-hal yang tidak dinyatakan dalam API mereka, dan, sebagai hasilnya, Lajang membuat API Anda menjadi pembohong patologis.

Di mana semua Singletons Gone menyatakan bahwa injeksi ketergantungan telah membuatnya mudah untuk mendapatkan instance untuk konstruktor yang membutuhkannya, yang mengurangi kebutuhan mendasar di balik yang buruk, Singletons global mengutuk dalam artikel pertama.

jason
sumber
8
Artikel-artikel Misko tentang masalah ini adalah hal terbaik yang beredar saat ini.
Chris Mayer
22
Tautan pertama sebenarnya tidak mengatasi masalah dengan lajang, melainkan mengasumsikan ketergantungan statis di dalam kelas. Mungkin untuk memperbaiki contoh yang diberikan dengan mengirimkan parameter, namun masih menggunakan lajang.
DGM
17
@ DMGM: Tepat - pada kenyataannya, ada pemutusan logis besar antara bagian 'alasan' dari artikel, dan bagian 'Singletons adalah penyebabnya'.
Harper Shelby
1
Artikel CreditCard Misko adalah contoh ekstrim dari penyalahgunaan pola ini.
Alex
2
Membaca artikel kedua Singletons sebenarnya adalah bagian dari model objek yang dijelaskan. Namun, alih-alih dapat diakses secara global, ini bersifat pribadi untuk objek Pabrik.
FruitBreak
121

Saya pikir kebingungan ini disebabkan oleh fakta bahwa orang tidak tahu aplikasi sebenarnya dari pola Singleton. Saya tidak bisa cukup menekankan hal ini. Singleton bukan pola untuk membungkus global. Pola singleton hanya boleh digunakan untuk menjamin bahwa satu dan hanya satu instance dari kelas yang diberikan ada selama run time.

Orang mengira Singleton jahat karena mereka menggunakannya untuk orang global. Karena kebingungan inilah Singleton dipandang rendah. Tolong, jangan bingung Lajang dan global. Jika digunakan untuk tujuan yang dimaksudkan, Anda akan mendapatkan manfaat ekstrem dari pola Singleton.

Cem Catikkas
sumber
20
Apa itu satu contoh, tersedia di seluruh aplikasi? Oh Global . "Singleton bukan pola untuk membungkus global" adalah naif atau menyesatkan, seperti definisi , pola membungkus kelas di sekitar turunan global.
cHao
26
Tentu saja Anda bebas membuat satu instance kelas ketika aplikasi Anda mulai, dan menyuntikkan instance itu, melalui antarmuka, ke dalam apa pun yang menggunakannya. Implementasinya seharusnya tidak peduli bahwa hanya ada satu.
Scott Whitlock
4
@ Rainius: Sebenarnya tidak ada, pada akhirnya. Tentu, Anda tidak bisa mengganti instansinya dengan sewenang-wenang. (Kecuali untuk saat-saat pemicu facepalm ketika Anda melakukannya . Saya benar-benar telah melihat setInstancemetode sebelumnya.) Namun, itu tidak masalah - bahwa Weenie yang "membutuhkan" seorang lajang juga tidak tahu apa-apa tentang enkapsulasi atau apa yang salah dengan enkapsulasi. negara global yang bisa berubah, jadi dia sangat membantu (?) menyediakan setter untuk setiap. tunggal. bidang. (Dan ya, ini terjadi. Banyak . Hampir setiap singleton yang pernah saya lihat di alam liar dapat berubah karena desain, dan seringkali memalukan.)
cHao
4
@Dainius: Untuk sebagian besar, kita sudah punya. "Lebih suka komposisi daripada warisan" telah menjadi hal yang cukup lama sekarang. Ketika warisan adalah terbukti solusi terbaik, meskipun, Anda tentu saja bebas untuk menggunakannya. Hal yang sama dengan lajang, global, threading, gotodll. Mereka mungkin bekerja dalam banyak kasus, tetapi terus terang, "bekerja" tidak cukup - jika Anda ingin bertentangan dengan kebijaksanaan konvensional, Anda sebaiknya dapat menunjukkan bagaimana pendekatan Anda adalah lebih baik daripada solusi konvensional. Dan saya belum melihat kasus seperti itu untuk pola Singleton.
cHao
3
Jangan sampai kita berbicara melewati satu sama lain, saya tidak hanya berbicara tentang contoh yang tersedia secara global. Ada banyak kasus untuk itu. Apa yang saya bicarakan (terutama ketika saya mengatakan "capital-S Singleton") adalah pola Singleton dari GoF, yang menanamkan instance global tunggal di dalam kelas itu sendiri, mengeksposnya melalui getInstancemetode yang serupa, dan mencegah keberadaan contoh kedua. Terus terang, pada titik itu, Anda mungkin bahkan tidak memiliki satu contoh.
cao
72

Satu hal yang agak buruk tentang lajang adalah Anda tidak dapat memperluasnya dengan mudah. Anda pada dasarnya harus membangun semacam pola dekorator atau semacamnya jika Anda ingin mengubah perilaku mereka. Juga, jika suatu hari Anda ingin memiliki banyak cara untuk melakukan satu hal itu, itu bisa agak menyakitkan untuk diubah, tergantung pada bagaimana Anda mengeluarkan kode Anda.

Satu hal yang perlu diperhatikan, jika Anda LAKUKAN menggunakan lajang, cobalah untuk mengirimkannya kepada siapa pun yang membutuhkannya daripada meminta mereka mengaksesnya secara langsung ... Jika tidak, jika Anda pernah memilih untuk memiliki banyak cara untuk melakukan hal yang dilakukan oleh lajang, itu akan menjadi agak sulit untuk diubah karena setiap kelas menanamkan ketergantungan jika mengakses langsung tunggal.

Pada dasarnya:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

daripada:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

Saya percaya pola semacam ini disebut injeksi ketergantungan dan umumnya dianggap sebagai hal yang baik.

Seperti pola apa pun ... Pikirkan dan pertimbangkan apakah penggunaannya dalam situasi yang diberikan tidak sesuai atau tidak ... Aturan dibuat untuk dilanggar biasanya, dan pola tidak boleh diterapkan tanpa tujuan tanpa berpikir.

Mike Stone
sumber
17
Heh, jika Anda melakukan ini di mana-mana, maka Anda harus menyerahkan referensi ke singelton di mana-mana juga, dan dengan demikian Anda tidak memiliki singleton lagi. :) (Yang biasanya merupakan hal yang baik IMO.)
Bjarke Freund-Hansen
2
@ BjarkeFreund-Hansen - Omong kosong, apa yang kamu bicarakan? Singleton hanyalah sebuah instance dari kelas yang dimundurkan satu kali. Merujuk Singleton seperti itu tidak menyalin objek yang sebenarnya, itu hanya referensi - Anda masih mendapatkan objek yang sama (baca: Singleton).
M. Mimpen
2
@ M.Mimpen: Tidak, huruf kapital-S Singleton (yang sedang dibahas di sini) adalah turunan dari kelas yang (a) menjamin bahwa hanya satu instance yang pernah ada, dan (b) diakses melalui kelas itu sendiri. titik akses global bawaan. Jika Anda telah menyatakan bahwa tidak ada panggilan getInstance(), maka (b) tidak lagi benar.
cHao
2
@ cHao Saya tidak mengikuti Anda atau Anda tidak mengerti kepada siapa saya berkomentar - yang bagi Bjarke Freund-Hansen. Bjarke menyatakan bahwa memiliki beberapa referensi dari hasil singleton memiliki beberapa lajang. Itu tentu tidak benar karena tidak ada salinan yang dalam.
M. Mimpen
6
@ M.Mimpen: Saya mengambil komentarnya untuk merujuk lebih banyak ke efek semantik. Setelah Anda melarang panggilan getInstance(), Anda telah secara efektif membuang satu perbedaan yang bermanfaat antara pola Singleton dan referensi biasa. Sejauh menyangkut kode, kelajangan bukan lagi properti. Hanya penelepon yang getInstance()pernah perlu tahu atau bahkan peduli berapa banyak contoh ada. Dengan hanya satu penelepon, perlu biaya lebih banyak dalam upaya dan fleksibilitas bagi kelas untuk menegakkan keterikatan secara andal daripada meminta penelepon hanya menyimpan referensi dan menggunakannya kembali.
cHao
69

Pola singleton bukanlah masalah tersendiri. Masalahnya adalah bahwa pola tersebut sering digunakan oleh orang-orang yang mengembangkan perangkat lunak dengan alat yang berorientasi objek tanpa memiliki pemahaman yang kuat tentang konsep OO. Ketika lajang diperkenalkan dalam konteks ini mereka cenderung tumbuh menjadi kelas yang tidak terkelola yang berisi metode pembantu untuk setiap penggunaan kecil.

Lajang juga merupakan masalah dari sudut pandang pengujian. Mereka cenderung membuat unit-test yang terisolasi sulit untuk ditulis. Inversion of control (IoC) dan injeksi ketergantungan adalah pola yang dimaksudkan untuk mengatasi masalah ini dengan cara yang berorientasi objek yang cocok untuk pengujian unit.

Dalam lingkungan sampah yang dikumpulkan, lajang dapat dengan cepat menjadi masalah sehubungan dengan manajemen memori.

Ada juga skenario multi-utas di mana lajang dapat menjadi hambatan serta masalah sinkronisasi.

Kimoz
sumber
4
saya tahu itu adalah thread yang berumur bertahun-tahun. hai @ Kimoz u berkata: - lajang dapat dengan cepat menjadi masalah sehubungan dengan manajemen memori. ingin menjelaskan secara lebih rinci masalah apa yang mungkin muncul terkait pengumpulan sampah tunggal & sampah.
Thomas
@ Kimimo, Pertanyaannya adalah bertanya, "Mengapa pola singleton itu sendiri tidak menjadi masalah?" dan Anda hanya mengulangi poinnya tetapi tidak menyediakan satu pun kasus penggunaan tunggal dari pola tunggal.
Pacerier
@ Thomas, karena singleton menurut definisi hanya ada dalam satu contoh. Jadi, seringkali rumit untuk menetapkan satu-satunya referensi ke null. Itu bisa dilakukan, tetapi itu berarti bahwa Anda sepenuhnya mengontrol titik di mana singleton tidak digunakan dalam aplikasi Anda. Dan ini jarang terjadi, dan biasanya sangat bertolak belakang dengan apa yang dicari para penggemar tunggal: cara sederhana untuk membuat satu instance selalu dapat diakses. Pada beberapa bingkai foto DI seperti Guice atau Dagger, tidak mungkin untuk menyingkirkan singleton dan tetap selamanya dalam memori. (Meskipun wadah yang disediakan lajang jauh lebih baik daripada yang dibuat di rumah).
Snicolas
54

Singleton diimplementasikan menggunakan metode statis. Metode statis dihindari oleh orang yang melakukan pengujian unit karena mereka tidak dapat diejek atau di-stub. Kebanyakan orang di situs ini adalah pendukung besar pengujian unit. Konvensi yang paling umum diterima untuk menghindarinya adalah menggunakan pola kontrol inversi .

Peter Mortensen
sumber
9
Itu terdengar lebih seperti masalah dengan pengujian unit yang dapat menguji objek (unit), fungsi (unit), seluruh perpustakaan (unit), tetapi gagal dengan apa pun statis di kelas (juga unit).
v010dya
2
bukankah Anda seharusnya meredam semua referensi eksternal? Jika iya, lalu apa masalah untuk moc singleton, jika tidak, apakah Anda benar-benar melakukan pengujian unit?
Dainius
@ Dainius: Contoh mengejek jauh lebih sedikit kesulitan daripada mengejek kelas Anda dapat mengekstraksi kelas yang sedang diuji dari sisa aplikasi dan menguji dengan Singletonkelas palsu . Namun, itu sangat menyulitkan proses pengujian. Untuk satu, sekarang Anda harus dapat membongkar kelas sesuka hati (bukan benar-benar pilihan dalam kebanyakan bahasa), atau memulai VM baru untuk setiap tes (baca: tes mungkin memakan waktu ribuan kali lebih lama). Namun untuk dua orang, ketergantungan pada Singletonadalah detail implementasi yang sekarang bocor di seluruh tes Anda.
cao
Powermock dapat mengejek hal-hal statis.
Snicolas
artinya mengejek suatu objek berarti membuat objek nyata? Jika mengejek tidak membuat objek nyata lalu mengapa itu penting apakah kelasnya tunggal atau metode statis?
Arun Raaj
45

Lajang juga buruk dalam hal pengelompokan . Karena itu, Anda tidak memiliki "persis satu singleton" dalam aplikasi Anda lagi.

Pertimbangkan situasi berikut: Sebagai pengembang, Anda harus membuat aplikasi web yang mengakses database. Untuk memastikan bahwa panggilan database bersamaan tidak saling bertentangan, Anda membuat thread-save SingletonDao:

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

Jadi, Anda yakin hanya ada satu singleton dalam aplikasi Anda dan semua basis data melewati satu-satunya ini SingletonDao. Lingkungan produksi Anda sekarang terlihat seperti ini: Singleton tunggal

Semuanya baik-baik saja sejauh ini.

Sekarang, pertimbangkan Anda ingin mengatur beberapa contoh aplikasi web Anda dalam sebuah cluster. Sekarang, Anda tiba-tiba memiliki sesuatu seperti ini:

Banyak lajang

Kedengarannya aneh, tetapi sekarang Anda memiliki banyak lajang dalam aplikasi Anda . Dan memang itulah yang seharusnya tidak dilakukan oleh seorang lajang: Memiliki banyak objek. Ini sangat buruk jika Anda, seperti ditunjukkan dalam contoh ini, ingin melakukan panggilan tersinkronisasi ke database.

Tentu saja ini adalah contoh dari penggunaan singleton yang buruk. Tetapi pesan dari contoh ini adalah: Anda tidak dapat mengandalkan bahwa ada satu contoh singleton dalam aplikasi Anda - terutama ketika menyangkut pengelompokan.

Uooo
sumber
4
jika Anda tidak tahu bagaimana menerapkan singleton, Anda seharusnya tidak melakukan itu. Jika Anda tidak tahu apa yang Anda lakukan, Anda harus menemukannya terlebih dahulu, dan hanya setelah itu lakukan apa yang Anda butuhkan.
Dainius
3
Ini menarik, saya punya pertanyaan. Jadi apa sebenarnya masalahnya jika lajang - masing-masing pada mesin yang berbeda / JVM - terhubung ke satu database? Lingkup lajang hanya untuk JVM tertentu, dan itu masih berlaku bahkan dalam sebuah cluster. Daripada hanya mengatakan secara filosofis bahwa situasi khusus ini buruk karena niat kami adalah objek tunggal di seluruh aplikasi, saya akan senang melihat masalah teknis yang dapat timbul karena pengaturan ini.
Saurabh Patil
39
  1. Ini mudah (ab) digunakan sebagai variabel global.
  2. Kelas yang bergantung pada lajang relatif lebih sulit untuk unit test secara terpisah.
jop
sumber
33

Monopoli adalah iblis dan lajang dengan keadaan yang tidak bisa dibaca / bisa berubah adalah masalah 'nyata' ...

Setelah membaca Singletons adalah Pendusta Patologis seperti yang disarankan dalam jawaban jason, saya menemukan berita gembira kecil ini yang memberikan contoh terbaik yang disajikan tentang bagaimana singleton sering disalahgunakan.

Global buruk karena:

  • Sebuah. Ini menyebabkan konflik namespace
  • b. Itu memperlihatkan negara dengan cara yang tidak beralasan

Ketika datang ke Singletons

  • Sebuah. Cara OO eksplisit memanggil mereka, mencegah konflik, jadi tunjuk a. bukan masalah
  • b. Lajang tanpa negara (seperti pabrik) tidak menjadi masalah. Lajang dengan status lagi dapat jatuh dalam dua kategori, mereka yang tidak dapat diubah atau menulis sekali dan membaca banyak (konfigurasi file properti). Ini tidak buruk. Lajang yang dapat berubah, yang merupakan jenis pemegang referensi adalah yang Anda bicarakan.

Dalam pernyataan terakhir dia merujuk pada konsep blog tentang 'lajang adalah pembohong'.

Bagaimana ini berlaku untuk Monopoli?

Untuk memulai permainan monopoli, pertama:

  • kami menetapkan aturan terlebih dahulu sehingga semua orang berada di halaman yang sama
  • setiap orang diberi awal yang sama di awal permainan
  • hanya satu set aturan yang disajikan untuk menghindari kebingungan
  • aturan tidak diperbolehkan untuk berubah sepanjang game

Sekarang, bagi siapa saja yang belum benar - benar bermain monopoli, standar-standar ini paling ideal. Kekalahan dalam monopoli sulit ditelan karena, monopoli adalah tentang uang, jika Anda kalah, Anda harus dengan susah payah menonton sisa pemain yang menyelesaikan permainan, dan kekalahan biasanya cepat dan menghancurkan. Jadi, aturan biasanya diputar di beberapa titik untuk melayani kepentingan diri sendiri dari beberapa pemain dengan mengorbankan yang lain.

Jadi, Anda bermain monopoli dengan teman-teman Bob, Joe, dan Ed. Anda dengan cepat membangun kerajaan Anda dan mengonsumsi pangsa pasar pada tingkat eksponensial. Lawan Anda melemah dan Anda mulai mencium bau darah (kiasan). Teman Anda, Bob, memasukkan semua uangnya ke dalam kemacetan sebanyak mungkin properti bernilai rendah, tetapi miliknya tidak menerima pengembalian investasi yang tinggi seperti yang ia harapkan. Bob, sebagai pukulan sial, mendarat di Boardwalk Anda dan dikeluarkan dari permainan.

Sekarang permainan beralih dari dadu-bergulir ramah ke bisnis yang serius. Bob telah menjadi contoh kegagalan dan Joe dan Ed tidak ingin berakhir seperti 'pria itu'. Jadi, menjadi pemain terkemuka Anda, tiba-tiba, menjadi musuh. Joe dan Ed mulai mempraktikkan perdagangan di bawah meja, suntikan uang di belakang, pertukaran rumah yang undervalued dan umumnya apa pun untuk melemahkan Anda sebagai pemain sampai salah satu dari mereka naik ke atas.

Kemudian, alih-alih salah satu dari mereka menang, prosesnya dimulai dari awal. Tiba-tiba, seperangkat aturan yang terbatas menjadi target yang bergerak dan permainan berubah menjadi jenis interaksi sosial yang akan menjadi dasar dari setiap acara TV realitas berperingkat tinggi sejak Survivor. Mengapa, karena peraturan berubah dan tidak ada konsensus tentang bagaimana / mengapa / apa yang seharusnya mereka wakili, dan yang lebih penting, tidak ada satu orang pun yang mengambil keputusan. Setiap pemain dalam permainan, pada saat itu, membuat peraturannya sendiri dan kekacauan terjadi sampai dua pemain terlalu lelah untuk mempertahankan permainan dan perlahan-lahan menyerah.

Jadi, jika buku aturan untuk permainan secara akurat mewakili singleton, buku aturan monopoli akan menjadi contoh penyalahgunaan.

Bagaimana ini berlaku untuk pemrograman?

Selain dari semua masalah keamanan dan sinkronisasi utas yang jelas yang hadir dalam lajang yang bisa berubah ... Jika Anda memiliki satu set data, yang dapat dibaca / dimanipulasi oleh berbagai sumber secara bersamaan dan ada selama masa eksekusi aplikasi, mungkin ini saat yang tepat untuk mundur dan bertanya "apakah saya menggunakan tipe data struktur yang tepat di sini".

Secara pribadi, saya telah melihat seorang programmer menyalahgunakan singleton dengan menggunakannya sebagai semacam toko database cross-thread twisted dalam suatu aplikasi. Setelah bekerja pada kode secara langsung, saya dapat membuktikan bahwa itu lambat (karena semua kunci utas diperlukan untuk membuatnya aman) dan mimpi buruk untuk dikerjakan (karena sifat bug sinkronisasi yang tidak dapat diprediksi / berselang), dan hampir tidak mungkin diuji dalam kondisi 'produksi'. Tentu, sebuah sistem bisa dikembangkan menggunakan polling / pensinyalan untuk mengatasi beberapa masalah kinerja tetapi itu tidak akan menyelesaikan masalah dengan pengujian dan, mengapa repot ketika database 'nyata' sudah dapat mencapai fungsi yang sama dalam jauh lebih kuat / Cara terukur.

Singleton hanya merupakan opsi jika Anda membutuhkan apa yang disediakan oleh singleton. Sebuah instance read-only read-only dari objek. Aturan yang sama harus mengalir ke properti / anggota objek juga.

Evan Plaice
sumber
1
Bagaimana jika singleton menguangkan beberapa data?
Yola
@Yola Ini akan mengurangi jumlah penulisan jika singleton dijadwalkan untuk memperbarui pada jadwal yang telah ditentukan dan / atau tetap, sehingga mengurangi pertikaian utas. Namun, Anda tidak akan dapat secara akurat menguji salah satu kode yang berinteraksi dengan singleton kecuali Anda mengejek instance non-singleton yang mensimulasikan penggunaan yang sama. Orang-orang TDD mungkin akan cocok tetapi itu akan berhasil.
Evan Plaice
@ EvanPlaice: Bahkan dengan tiruan, Anda akan memiliki beberapa masalah dengan kode yang mengatakan Singleton.getInstance(). Bahasa yang mendukung refleksi mungkin dapat mengatasinya dengan mengatur bidang tempat One True Instance disimpan. IMO, bagaimanapun, tes menjadi sedikit kurang dapat dipercaya setelah Anda melakukan monkeying dengan negara pribadi kelas lain.
cao
28

Singleton bukan tentang satu contoh!

Tidak seperti jawaban lain saya tidak ingin berbicara tentang apa yang salah dengan Singletons tetapi untuk menunjukkan kepada Anda betapa kuat dan mengagumkannya mereka jika digunakan dengan benar!

  • Masalah : Singleton dapat menjadi tantangan di lingkungan multi-threading
    Solusi : Gunakan proses bootstrap berulir tunggal untuk menginisialisasi semua dependensi singleton Anda.
  • Masalah : Sulit untuk mengejek lajang.
    Solusi : Gunakan metode Pola pabrik untuk mengejek

Anda dapat memetakan MyModelkeTestMyModel kelas yang mewarisinya, di mana pun saat MyModelakan disuntikkan Anda akan mendapatkan TestMyModelinstread. - Masalah : Lajang dapat menyebabkan kebocoran memori karena tidak pernah dibuang.
Solusi : Baiklah, buang mereka! Menerapkan panggilan balik di aplikasi Anda untuk membuang lajang dengan benar, Anda harus menghapus data apa pun yang terhubung dengannya dan akhirnya: menghapusnya dari Pabrik.

Seperti yang saya nyatakan pada judul singleton bukan tentang satu contoh.

  • Lajang meningkatkan keterbacaan : Anda dapat melihat kelas Anda dan melihat singleton apa yang disuntikkan untuk mencari tahu apa itu dependensi.
  • Lajang meningkatkan pemeliharaan : Setelah Anda menghapus ketergantungan dari kelas Anda baru saja menghapus beberapa injeksi tunggal, Anda tidak perlu pergi dan mengedit tautan besar dari kelas lain yang baru saja memindahkan ketergantungan Anda (Ini adalah kode bau untuk saya @ Jim Burger )
  • Singletons meningkatkan memori dan kinerja : Ketika beberapa hal terjadi dalam aplikasi Anda, dan dibutuhkan waktu panggilan balik yang lama untuk dikirim, Anda membuang-buang memori dan kinerja, dengan menggunakan Singleton Anda memotong perantara, dan meningkatkan kinerja dan penggunaan memori Anda ( dengan menghindari alokasi variabel lokal yang tidak perlu).
Ilya Gazman
sumber
2
Ini tidak membahas masalah utama saya dengan Singletons yang memungkinkan mereka mengakses negara global dari ribuan kelas di proyek Anda.
LegendLength
8
Itu akan menjadi tujuannya ...
Ilya Gazman
LegendLength Mengapa itu salah? Misalnya, dalam aplikasi saya, saya memiliki Settingsobjek tunggal yang dapat diakses dari widget apa pun sehingga setiap widget tahu tentang cara memformat angka yang ditampilkan. Jika itu bukan objek yang dapat diakses secara global, saya harus menyuntikkannya dalam konstruktor ke setiap widget di konstruktor dan menyimpan referensi sebagai variabel anggota. Ini adalah pemborosan memori dan waktu yang mengerikan.
VK
23

Jawaban saya tentang bagaimana Singletons buruk selalu, "mereka sulit dilakukan dengan benar". Banyak komponen dasar bahasa adalah singleton (kelas, fungsi, ruang nama dan bahkan operator), seperti komponen dalam aspek lain dari komputasi (localhost, rute default, sistem file virtual, dll.), Dan itu bukan kebetulan. Walaupun mereka menyebabkan masalah dan frustrasi dari waktu ke waktu, mereka juga dapat membuat banyak hal bekerja lebih baik.

Dua gangguan terbesar yang saya lihat adalah: memperlakukannya seperti global & gagal mendefinisikan penutupan Singleton.

Semua orang berbicara tentang Singleton sebagai global, karena pada dasarnya memang demikian. Namun, banyak (sayangnya, tidak semua) dari keburukan di global datang secara intrinsik bukan dari menjadi global, tetapi bagaimana Anda menggunakannya. Hal yang sama berlaku untuk para lajang. Sebenarnya lebih sebagai "contoh tunggal" benar-benar tidak perlu berarti "dapat diakses secara global". Ini lebih merupakan produk sampingan alami, dan mengingat semua hal buruk yang kita tahu berasal darinya, kita tidak boleh terburu-buru mengeksploitasi aksesibilitas global. Setelah programmer melihat Singleton mereka sepertinya selalu mengaksesnya secara langsung melalui metode instance-nya. Sebagai gantinya, Anda harus menavigasi ke sana seperti Anda akan objek lain. Sebagian besar kode bahkan tidak harus sadar bahwa itu berurusan dengan Singleton (longgar, kan?). Jika hanya sedikit kode mengakses objek seperti itu adalah global, banyak kerusakan yang dibatalkan.

Konteks Singleton juga sangat penting. Karakteristik yang menentukan dari Singleton adalah bahwa ada "hanya satu", tetapi kenyataannya adalah "hanya satu" dalam semacam konteks / namespace. Mereka biasanya salah satu dari: satu per utas, proses, alamat IP atau cluster, tetapi juga bisa satu per prosesor, mesin, bahasa namespace / kelas loader / apa pun, subnet, Internet, dll.

Kesalahan lainnya, yang kurang umum, adalah mengabaikan gaya hidup Singleton. Hanya karena hanya ada satu tidak berarti Singleton adalah mahakuasa "selalu ada dan akan selalu ada", juga tidak secara umum diinginkan (objek tanpa awal dan akhir melanggar semua jenis asumsi yang berguna dalam kode, dan harus digunakan hanya dalam situasi yang paling menyedihkan.

Jika Anda menghindari kesalahan-kesalahan itu, Lajang masih bisa menjadi PITA, sedikit itu siap untuk melihat banyak masalah terburuk dikurangi secara signifikan. Bayangkan Java Singleton, yang secara eksplisit didefinisikan sebagai sekali per classloader (yang berarti membutuhkan kebijakan keselamatan ulir), dengan metode penciptaan dan penghancuran yang ditentukan dan siklus hidup yang menentukan kapan dan bagaimana mereka dipanggil, dan yang metode "instance" memiliki paket perlindungan sehingga umumnya diakses melalui objek non-global lainnya. Masih merupakan sumber masalah yang potensial, tetapi tentu saja jauh lebih sedikit masalah.

Sedihnya, alih-alih mengajarkan contoh yang baik tentang bagaimana melakukan lajang. Kami mengajarkan contoh-contoh buruk, membiarkan programmer menggunakan mereka untuk sementara waktu, dan kemudian memberi tahu mereka bahwa mereka adalah pola desain yang buruk.

Christopher Smith
sumber
23

Lihat Wikipedia Singleton_pattern

Ini juga dianggap sebagai anti-pola oleh beberapa orang, yang merasa bahwa itu terlalu digunakan, memperkenalkan batasan yang tidak perlu dalam situasi di mana satu-satunya contoh kelas tidak benar-benar diperlukan. [1] [2] [3] [4]

Referensi (hanya referensi yang relevan dari artikel)

  1. ^ Alex Miller. Pola yang saya benci # 1: Singleton , Juli 2007
  2. ^ Scott Densmore. Mengapa lajang jahat , Mei 2004
  3. ^ Steve Yegge. Lajang dianggap bodoh , September 2004
  4. ^ JB Rainsberger, IBM. Gunakan lajang Anda dengan bijak , Juli 2001
GUI Junkie
sumber
8
Penjelasan tentang pola itu tidak menjelaskan mengapa itu dianggap sebagai kejahatan ...
Jrgns
2
Hampir tidak adil: "Ini juga dianggap sebagai anti-pola oleh beberapa orang, yang merasa bahwa itu terlalu digunakan, memperkenalkan batasan yang tidak perlu dalam situasi di mana satu-satunya contoh kelas tidak benar-benar diperlukan." Lihatlah referensi ... Pokoknya ada RTFM bagi saya.
GUI Junkie
17

Bukannya lajang itu sendiri buruk tetapi pola desain GoF adalah. Satu-satunya argumen yang benar-benar valid adalah bahwa pola desain GoF tidak cocok untuk pengujian, terutama jika tes dijalankan secara paralel.

Menggunakan instance tunggal dari kelas adalah konstruk yang valid selama Anda menerapkan cara berikut dalam kode:

  1. Pastikan kelas yang akan digunakan sebagai singleton mengimplementasikan antarmuka. Ini memungkinkan stubs atau ejekan untuk diimplementasikan menggunakan antarmuka yang sama

  2. Pastikan Singleton aman dari utas benang. Itu diberikan.

  3. Singleton harus sederhana dan tidak terlalu rumit.

  4. Selama runtime aplikasi Anda, di mana lajang perlu dikirimkan ke objek tertentu, gunakan pabrik kelas yang membangun objek itu dan minta pabrik kelas meneruskan instance singleton ke kelas yang membutuhkannya.

  5. Selama pengujian dan untuk memastikan perilaku deterministik, buat kelas singleton sebagai contoh terpisah sebagai kelas aktual itu sendiri atau sebuah rintisan / mock yang mengimplementasikan perilakunya dan meneruskannya seperti halnya ke kelas yang membutuhkannya. Jangan gunakan faktor kelas yang menciptakan objek yang diuji yang membutuhkan singleton selama pengujian karena akan melewati instance global tunggal dari itu, yang mengalahkan tujuannya.

Kami telah menggunakan Singletons dalam solusi kami dengan banyak keberhasilan yang dapat diuji memastikan perilaku deterministik dalam aliran uji coba paralel.

pengguna1578119
sumber
+1, Akhirnya jawaban yang ditujukan ketika seorang singleton mungkin valid.
Pacerier
16

Saya ingin membahas 4 poin dalam jawaban yang diterima, semoga seseorang dapat menjelaskan mengapa saya salah.

  1. Mengapa menyembunyikan dependensi dalam kode Anda buruk? Sudah ada puluhan dependensi tersembunyi (panggilan runtime C, panggilan OS API, panggilan fungsi global), dan dependensi singleton mudah ditemukan (cari misalnya ()).

    "Membuat sesuatu yang global untuk menghindari menyebarkannya adalah bau kode." Mengapa tidak melewatkan sesuatu untuk menghindari membuatnya menjadi bau kode?

    Jika Anda melewatkan objek melalui 10 fungsi dalam tumpukan panggilan hanya untuk menghindari singleton, apakah itu hebat?

  2. Prinsip Tanggung Jawab Tunggal: Saya pikir ini agak kabur dan tergantung pada definisi tanggung jawab Anda. Pertanyaan yang relevan adalah, mengapa menambahkan "tanggung jawab" khusus ini ke masalah kelas?

  3. Mengapa melewatkan objek ke kelas membuatnya lebih erat daripada menggunakan objek itu sebagai singleton dari dalam kelas?

  4. Mengapa itu berubah berapa lama negara berlangsung? Lajang dapat dibuat atau dimusnahkan secara manual, sehingga kontrolnya masih ada, dan Anda dapat membuat masa pakai itu sama dengan masa hidup objek yang bukan tunggal.

Mengenai tes unit:

  • tidak semua kelas perlu diuji unit
  • tidak semua kelas yang perlu diuji unit perlu mengubah implementasi singleton
  • jika mereka lakukan perlu unit diuji dan melakukan perlu mengubah pelaksanaan, sangat mudah untuk mengubah kelas dari menggunakan tunggal untuk memiliki tunggal berlalu untuk itu melalui injeksi ketergantungan.
Jon
sumber
1
1. Semua dependensi tersembunyi itu? Itu juga buruk. Ketergantungan tersembunyi selalu jahat. Tetapi dalam kasus CRT dan OS, mereka sudah ada di sana, dan mereka satu-satunya cara untuk menyelesaikan sesuatu. (Cobalah menulis program C tanpa menggunakan runtime atau OS.) Kemampuan itu untuk melakukan hal-hal yang jauh melebihi negatifnya. Lajang dalam kode kami tidak mendapatkan kemewahan itu; karena mereka dengan kuat berada dalam lingkup kendali dan tanggung jawab kami, setiap penggunaan (baca: setiap ketergantungan tersembunyi tambahan) harus dapat dibenarkan sebagai satu-satunya cara yang masuk akal untuk menyelesaikan pekerjaan. Sangat, sangat sedikit dari mereka yang sebenarnya.
cHao
2. "Tanggung jawab" biasanya didefinisikan sebagai "alasan untuk berubah". Seorang lajang akhirnya mengelola hidupnya dan melakukan pekerjaannya yang sebenarnya. Jika Anda mengubah cara pekerjaan dilakukan atau bagaimana grafik objek dibangun, Anda harus mengubah kelas. Tetapi jika Anda memiliki kode lain, buatlah grafik objek, dan kelas Anda hanya melakukan tugasnya yang sebenarnya, kode inisialisasi itu dapat mengatur grafik objek sesuai keinginannya. Semuanya jauh lebih modular dan dapat diuji, karena Anda dapat memasukkan uji ganda dan merobohkan semuanya sesuka hati, dan Anda tidak lagi mengandalkan bagian bergerak yang tersembunyi.
cHao
1
@ cHao: 1. Baik bahwa Anda mengenali bahwa "kemampuan untuk melakukan hal-hal jauh melebihi negatifnya." Contohnya adalah kerangka kerja logging. Adalah jahat untuk mengamanatkan melewati objek logging global dengan injeksi ketergantungan melalui lapisan-lapisan pemanggilan fungsi jika itu berarti bahwa para pengembang tidak disarankan untuk melakukan logging. Karena itu, singleton. 3. Titik kopling ketat Anda hanya relevan jika Anda ingin klien kelas mengontrol perilaku melalui polimorfisme. Itu sering tidak terjadi.
Jon
2
4. Saya tidak yakin bahwa "tidak dapat dihancurkan secara manual" adalah implikasi logis dari "hanya satu contoh". Jika itu cara Anda mendefinisikan singleton, maka kita tidak membicarakan hal yang sama. Semua kelas tidak harus diuji unit. Itu keputusan bisnis, dan kadang-kadang waktu ke pasar atau biaya pengembangan akan diutamakan.
Jon
1
3. Jika Anda tidak ingin polimorfisme, maka Anda bahkan tidak perlu sebuah instance. Anda mungkin juga membuatnya menjadi kelas statis, karena Anda mengikat diri Anda pada satu objek dan implementasi tunggal - dan setidaknya API tidak berbohong kepada Anda.
cHao
15

Vince Huston memiliki kriteria ini, yang menurut saya masuk akal:

Singleton harus dipertimbangkan hanya jika ketiga kriteria berikut dipenuhi:

  • Kepemilikan instance tunggal tidak dapat ditetapkan secara wajar
  • Inisialisasi malas diinginkan
  • Akses global tidak disediakan

Jika kepemilikan single instance, kapan dan bagaimana inisialisasi terjadi, dan akses global tidak menjadi masalah, Singleton tidak cukup menarik.

js.
sumber
14

Lajang buruk dari sudut pandang murni.

Dari sudut pandang praktis, singleton adalah trade-off yang mengembangkan waktu vs kompleksitas .

Jika Anda tahu aplikasi Anda tidak akan banyak berubah, mereka cukup baik untuk digunakan. Hanya tahu bahwa Anda mungkin perlu memperbaiki keadaan jika persyaratan Anda berubah secara tak terduga (yang cukup baik dalam kebanyakan kasus).

Lajang terkadang juga mempersulit pengujian unit .

tacone
sumber
3
Pengalaman saya adalah bahwa memulai dengan lajang benar-benar akan menyakiti Anda bahkan dalam jangka pendek, tidak termasuk jangka panjang. Efek ini mungkin saya kurang begitu jika aplikasi sudah ada selama beberapa waktu, dan mungkin sudah penuh dengan lajang lainnya. Mulai dari awal? Jauhi mereka apapun situasinya! Ingin satu contoh objek? Biarkan pabrik mengelola instantiasi objek ini.
Sven
1
AFAIK Singleton adalah metode pabrik. Jadi saya tidak mendapatkan rekomendasi Anda.
tacone
3
"A factory"! = "Sebuah factory_method yang tidak dapat dipisahkan dari hal-hal berguna lainnya di kelas yang sama"
Sven
13

Tidak ada yang salah secara inheren dengan polanya, dengan asumsi itu sedang digunakan untuk beberapa aspek dari model Anda yang benar-benar tunggal.

Saya percaya serangan balik itu karena terlalu sering digunakan, yang pada gilirannya, disebabkan oleh fakta bahwa itu adalah pola termudah untuk dipahami dan diterapkan.

Ben Hoffstein
sumber
6
Saya pikir reaksi lebih berkaitan dengan orang-orang yang berlatih pengujian unit formal menyadari bahwa mereka adalah mimpi buruk untuk dihadapi.
Jim Burger
1
Tidak yakin mengapa ini pada -1. Itu bukan jawaban yang paling deskriptif tetapi dia juga tidak salah.
Outlaw Programmer
13

Saya tidak akan mengomentari argumen baik / jahat, tapi saya belum menggunakannya sejak Spring datang. Menggunakan injeksi ketergantungan telah cukup banyak menghilangkan persyaratan saya untuk singleton, servicelocators dan pabrik. Saya menemukan ini lingkungan yang jauh lebih produktif dan bersih, setidaknya untuk jenis pekerjaan yang saya lakukan (aplikasi web berbasis Java).

Peter Mortensen
sumber
3
bukan Spring spring, secara default, Lajang?
langsing
3
Ya, tapi maksud saya 'coding' lajang. Saya belum 'menulis singleton' (yaitu dengan konstruktor pribadi yada yada yada sesuai pola desain) - tapi ya, dengan pegas saya menggunakan contoh tunggal.
Prule
4
jadi ketika orang lain melakukannya, tidak apa-apa (jika saya akan menggunakannya setelah itu), tetapi jika orang lain melakukannya dan saya tidak akan menggunakannya, itu jahat?
Dainius
11

Singleton adalah sebuah pola dan dapat digunakan atau disalahgunakan seperti alat lainnya.

Bagian buruk dari singleton umumnya adalah pengguna (atau harus saya katakan penggunaan singleton yang tidak tepat untuk hal-hal yang tidak dirancang untuk dilakukan). Pelaku terbesar menggunakan singleton sebagai variabel global palsu.

Loki Astari
sumber
1
Terima kasih Loki :) Persis saya. Perburuan seluruh penyihir mengingatkan saya pada perdebatan goto. Alat adalah untuk orang yang tahu cara menggunakannya; menggunakan alat yang tidak Anda sukai mungkin berbahaya bagi Anda jadi hindarilah tetapi JANGAN minta orang lain untuk tidak menggunakan / tidak belajar menggunakannya dengan benar. Saya tidak benar-benar "pro-singleton" tetapi saya menyukai kenyataan bahwa saya memiliki alat seperti itu dan saya bisa merasakan di tempat yang tepat untuk menggunakannya. Titik.
dkellner
8

Ketika Anda menulis kode menggunakan lajang, katakanlah, logger atau koneksi database, dan setelah itu Anda menemukan Anda membutuhkan lebih dari satu log atau lebih dari satu database, Anda dalam masalah.

Lajang membuatnya sangat sulit untuk berpindah dari mereka ke benda biasa.

Juga, terlalu mudah untuk menulis singleton yang tidak aman.

Daripada menggunakan lajang, Anda harus melewati semua objek utilitas yang diperlukan dari fungsi ke fungsi. Itu bisa disederhanakan jika Anda membungkus semuanya menjadi objek pembantu, seperti ini:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}
Roman Odaisky
sumber
4
Melewati argumen untuk setiap metode brilian, itu harus menuju setidaknya 10 cara teratas untuk mencemari api Anda.
Dainius
Itu adalah kelemahan yang jelas, dan yang seharusnya ditangani oleh bahasa, melalui argumen yang entah bagaimana menyebar ke panggilan bersarang, tetapi jika tidak ada dukungan seperti itu, itu harus dilakukan secara manual. Pertimbangkan bahwa bahkan lajang alami, seperti objek yang mewakili jam sistem, memiliki potensi masalah. Misalnya, bagaimana Anda akan mencakup kode yang bergantung pada jam (pikirkan meteran listrik berganda) dengan pengujian?
Roman Odaisky
tergantung apa yang Anda uji, jika Anda tidak menguji singleton, mengapa Anda peduli bagaimana perilakunya, jika 2 objek jarak jauh Anda bergantung pada perilaku satu sama lain, itu bukan masalah tunggal ..
Dainius
Bisakah Anda memberikan bahasa apa yang akan disebarkan ke panggilan bersarang?
Dainius
1
Jika Anda memiliki satu modul yang tergantung pada yang lain dan Anda tidak tahu / tidak bisa mengacaukannya, Anda akan sulit terlepas apakah itu tunggal atau tidak. Orang sering menggunakan cara pewarisan, di mana mereka harus menggunakan komposisi, tetapi Anda tidak mengatakan bahwa warisan itu buruk, dan OOP harus dihindari dengan cara apa pun, karena begitu banyak orang melakukan kesalahan desain di sana, atau Anda?
Dainius
8

Masalah dengan lajang adalah masalah peningkatan cakupan dan karena itu penggandengan . Tidak dapat disangkal bahwa ada beberapa situasi di mana Anda memang membutuhkan akses ke satu contoh, dan itu dapat dilakukan dengan cara lain.

Saya sekarang lebih suka mendesain di sekitar wadah inversi kontrol (IOC) dan memungkinkan masa hidup dikontrol oleh wadah. Ini memberi Anda manfaat dari kelas-kelas yang bergantung pada instance untuk tidak menyadari fakta bahwa ada satu instance. Masa hidup singleton dapat diubah di masa depan. Setelah contoh seperti yang saya temui baru-baru ini adalah penyesuaian yang mudah dari single threaded ke multi-threaded.

FWIW, jika PIA ketika Anda mencoba untuk mengujinya maka itu akan menjadi PIA ketika Anda mencoba untuk debug, memperbaiki bug atau meningkatkannya.

Mark Lindell
sumber
7

Lajang TIDAK buruk. Ini hanya buruk ketika Anda membuat sesuatu yang unik secara global yang tidak unik secara global.

Namun, ada "layanan lingkup aplikasi" (pikirkan sistem pesan yang membuat komponen berinteraksi) - PANGGILAN ini untuk singleton, "MessageQueue" - kelas yang memiliki metode "SendMessage (...)".

Anda kemudian dapat melakukan yang berikut dari semua tempat:

MessageQueue.Current.SendMessage (MailArrivedMessage (...)) baru;

Dan, tentu saja, lakukan:

MessageQueue.Current.RegisterReceiver (ini);

di kelas yang mengimplementasikan IMessageReceiver.

StormianRootSolver
sumber
6
Dan jika saya ingin membuat antrian pesan kedua, dengan cakupan yang lebih kecil, mengapa saya tidak boleh menggunakan kembali kode Anda untuk membuatnya? Seorang lajang mencegah hal itu. Tetapi jika Anda baru saja membuat kelas antrian pesan reguler, dan kemudian membuat satu instance global untuk bertindak sebagai "ruang lingkup aplikasi", saya bisa membuat instance kedua untuk penggunaan lainnya. Tetapi jika Anda menjadikan kelas sebagai singleton, saya harus menulis kelas antrian pesan kedua .
Jalf
1
Juga mengapa kita tidak hanya memiliki Daftar Pelanggan global sehingga kami dapat menyebutnya dengan baik dari mana saja seperti MessageQueue? Saya percaya jawabannya sama untuk keduanya: Secara efektif membuat variabel global.
LegendLength
7

Terlalu banyak orang menaruh benda yang tidak aman dalam pola tunggal. Saya telah melihat contoh-contoh DataContext ( LINQ to SQL ) yang dilakukan dalam pola tunggal, meskipun fakta bahwa DataContext tidak aman dan murni objek unit kerja.

Peter Mortensen
sumber
banyak orang menulis kode multi-utas yang tidak aman, apakah itu berarti kita harus menghilangkan utas?
Dainius
@Dainius: Sebenarnya, ya. IMO model konkurensi default harus multi-proses, dengan cara dua proses untuk (1) saling mengirimkan pesan dengan mudah, dan / atau (2) berbagi memori berdasarkan kebutuhan. Threading berguna jika Anda ingin membagikan segalanya , tetapi Anda tidak pernah benar-benar menginginkannya. Itu bisa ditiru dengan berbagi seluruh ruang alamat, tetapi itu juga akan dianggap sebagai anti-pola.
cao
@Dainius: Dan tentu saja, dengan asumsi bahwa konkurensi jujur-ke-kebaikan diperlukan sama sekali. Seringkali yang Anda inginkan adalah dapat melakukan satu hal sambil menunggu OS melakukan hal lain. (Memperbarui UI saat membaca dari soket, misalnya.) API async yang layak bisa membuat pengerjaan threading penuh tidak diperlukan dalam sebagian besar kasus.
cao
jadi jika Anda memiliki matriks besar data, yang Anda perlukan proses, lebih baik melakukannya dalam satu utas, karena bagi banyak orang mungkin melakukan itu salah ..
Dainius
@Dainius: Dalam banyak kasus, ya. (Threading sebenarnya bisa memperlambat Anda , jika Anda menambahkan sinkronisasi ke dalam campuran. Ini adalah contoh utama mengapa multithreading tidak seharusnya menjadi pemikiran pertama kebanyakan orang.) Dalam kasus lain, akan lebih baik untuk memiliki dua proses yang hanya berbagi barang yang dibutuhkan. Tentu saja, Anda harus mengatur proses untuk berbagi memori. Tapi terus terang, saya melihat itu sebagai hal yang baik - jauh lebih baik, setidaknya, daripada default ke mode berbagi-semuanya. Ini mengharuskan Anda untuk secara eksplisit mengatakan (dan, idealnya, untuk mengetahui) bagian mana yang dibagikan, dan dengan demikian bagian mana yang perlu diamankan.
cHao
6

Ini satu hal lagi tentang lajang yang belum ada yang mengatakan.

Dalam kebanyakan kasus "singletonity" adalah detail implementasi untuk beberapa kelas daripada karakteristik antarmuka-nya. Pembalikan Wadah Kontrol dapat menyembunyikan karakteristik ini dari pengguna kelas; Anda hanya perlu menandai kelas Anda sebagai singleton (dengan @Singletonanotasi di Jawa misalnya) dan hanya itu; IoCC akan melakukan sisanya. Anda tidak perlu menyediakan akses global ke instance singleton Anda karena akses sudah dikelola oleh IoCC. Dengan demikian tidak ada yang salah dengan IoC Singletons.

GoF Singletons berlawanan dengan IoC Singletons seharusnya mengekspos "singletonity" di antarmuka melalui metode getInstance (), dan agar mereka menderita dari semua yang dikatakan di atas.

Volodymyr Frolov
sumber
3
Menurut pendapat saya, "singletonity" adalah detail lingkungan run-time, dan tidak boleh dipertimbangkan oleh programmer yang menulis kode kelas. Sebaliknya, itu adalah pertimbangan yang harus dibuat oleh kelas USER. hanya PENGGUNA yang tahu berapa banyak contoh yang benar-benar diperlukan.
Earth Engine
5

Lajang tidak jahat, jika Anda menggunakannya dengan benar & minimal . Ada banyak pola desain bagus lainnya yang menggantikan kebutuhan singleton di beberapa titik (& juga memberikan hasil terbaik). Tetapi beberapa programmer tidak menyadari pola-pola yang baik & menggunakan singleton untuk semua kasus yang membuat singleton jahat bagi mereka.

Sriram
sumber
Luar biasa! Sangat setuju! Tetapi Anda bisa, dan mungkin harus, menguraikan lebih banyak. Seperti memperluas pola desain mana yang paling sering diabaikan dan bagaimana "benar dan minimal" menggunakan lajang. Lebih mudah diucapkan daripada dilakukan ! : P
cregox
Pada dasarnya alternatifnya adalah untuk melewatkan objek di sekitar melalui parameter metode, daripada mengaksesnya melalui keadaan global.
LegendLength
5

Pertama kelas dan kolaboratornya harus terlebih dahulu melakukan tujuan yang dimaksudkan daripada berfokus pada tanggungan. Manajemen siklus hidup (ketika instance dibuat dan ketika mereka keluar dari ruang lingkup) tidak boleh menjadi bagian dari tanggung jawab kelas. Praktik terbaik yang diterima untuk ini adalah membuat atau mengonfigurasi komponen baru untuk mengelola dependensi menggunakan injeksi dependensi.

Seringkali perangkat lunak menjadi lebih rumit, masuk akal untuk memiliki beberapa instance independen dari kelas Singleton dengan status yang berbeda. Mengkomit kode untuk mengambil singleton salah dalam kasus seperti itu. Menggunakan Singleton.getInstance()mungkin ok untuk sistem kecil sederhana tetapi tidak bekerja / skala ketika seseorang mungkin membutuhkan contoh berbeda dari kelas yang sama.

Tidak ada kelas yang harus dianggap sebagai singleton melainkan yang seharusnya merupakan aplikasi penggunaannya atau bagaimana itu digunakan untuk mengkonfigurasi tanggungan. Untuk yang cepat dan buruk ini tidak masalah - hanya luke hard coding mengatakan path file tidak masalah tetapi untuk aplikasi yang lebih besar ketergantungan seperti itu harus diperhitungkan dan dikelola dengan cara yang lebih tepat menggunakan DI.

Masalah yang disebabkan oleh singleton dalam pengujian adalah gejala dari kasus / lingkungan penggunaan tunggal berkode keras. Test suite dan banyak tes adalah masing-masing individu dan memisahkan sesuatu yang tidak kompatibel dengan pengkodean tunggal.

mP.
sumber
4

Karena mereka pada dasarnya adalah variabel global berorientasi objek, Anda biasanya dapat merancang kelas Anda sedemikian rupa sehingga Anda tidak membutuhkannya.

Ycros
sumber
Jika kelas Anda tidak terbatas hanya satu kali, Anda perlu anggota statis di kelas Anda yang dikelola oleh semaphores yang menghasilkan hal yang hampir sama! Apa alternatif yang Anda usulkan?
Jeach
Saya memiliki aplikasi besar dengan "MainScreen" layar ini membuka banyak bentuk modal / non modal / UI yang lebih kecil. IMHO Saya merasa bahwa MainScreen harus tunggal sehingga misalnya, widget di suatu tempat di sudut jauh aplikasi ingin menunjukkan statusnya di bilah status MainScreen, yang harus dilakukan adalah MainScreen.getInstance (). SetStatus () "Some Text"); Apa yang Anda usulkan sebagai alternatif? Lewati MainScreen di seluruh aplikasi ?? : D
Salvin Francis
2
@SalvinFrancis: Apa yang saya sarankan adalah, Anda berhenti memiliki objek yang peduli tentang hal-hal yang seharusnya tidak mereka pedulikan dan menyelinap di aplikasi untuk bermain-main dengan satu sama lain. :) Contoh Anda akan jauh lebih baik dilakukan dengan acara. Ketika Anda melakukan acara dengan benar, sebuah widget bahkan tidak perlu peduli apakah ada Layar Utama sama sekali; itu hanya menyiarkan "hei, hal-hal terjadi", dan apa pun yang telah berlangganan acara "hal-hal terjadi" (baik itu MainScreen, WidgetTest, atau yang lainnya sama sekali!) memutuskan bagaimana ia ingin merespons. Begitulah seharusnya OOP dilakukan. :)
cHao
1
@ Salvin Pertimbangkan betapa sulitnya alasan tentang MainScreen saat debugging jika sedang 'diam-diam' diperbarui oleh banyak komponen. Teladan Anda adalah alasan sempurna mengapa lajang buruk.
LegendLength