Singletonlar hakkında bu kadar kötü olan ne? [kapalı]


1983

Singleton deseni bir tamamen ödenmiş üyesidir GoF 'ın desenler kitabında , ancak son zamanlarda oldukça geliştirici dünya tarafından yetim görünüyor. Özellikle fabrika sınıfları için hala çok fazla singleton kullanıyorum ve multithreading konularında (aslında herhangi bir sınıf gibi) biraz dikkatli olmak zorunda kalırken, neden bu kadar korkunç olduklarını göremiyorum.

Yığın Taşması özellikle herkesin Singletons'un kötü olduğunu kabul ettiğini varsayar. Neden?

Lütfen cevaplarınızı " gerçekler, referanslar veya özel uzmanlık " ile destekleyin


6
Kodu uyarlamaya çalıştığım için tek bir tasarım kullanmanın beni son zamanlarda yaktığını söylemeliyim. Boş zamanlarımda yaptığım gibi, bunu yeniden düzenlemek için neredeyse çok tembelim. Verimlilik için kötü haber.
Marcin

71
Cevaplarda çok fazla 'eksileri' var, ancak modelin iyi olduğu zaman, kötülerin aksine, bazı iyi örnekler görmek istiyorum ...
DGM

50
Birkaç ay önce konuyla ilgili bir blog yazısı yazdım: jalf.dk/blog/2010/03/… - ve bunu açıkça söyleyeyim. Şahsen tek bir durumun doğru çözüm olduğu tek bir durumu düşünemiyorum. Bu, böyle bir durumun mevcut olmadığı anlamına gelmez, ancak ... onları nadir olarak adlandırmak bir eksikliktir.
jalf

8
O anlamına gelmez @AdamSmith sen var etmek, ancak bu demek olabilir bunu böyle erişin. Ve eğer bu şekilde erişmek istemiyorsanız, o zaman onu tek başına yapmak için çok az neden var. Yani argümanınız etkili bir şekilde "tekil olarak davranmazsak bir tekil yapmanın zararı yoktur . Evet, harika. Arabam da araba kullanmazsam kirlenmez. Ama sonra sadece ilk etapta bir araba
edinmemek

35
Bu konunun en kötü yanı, singletonlardan nefret edenlerin nadiren bunun yerine ne kullanmaları gerektiğine dair somut önerilerde bulunmalarıdır. Bütün bu SO makale aracılığıyla dergi makaleleri ve kendini yayınlanan bloglara bağlantılar, örneğin, üzerinde gidip neden üzerinde değil (hepsi mükemmel nedenleri konum ve) kullanımı tekiz, ancak değiştirmeler üzerinde son derece ince konum. Yine de bir sürü el yıkama. Yeni programcılara neden singleton kullanmamaya çalıştığımız, işaret edecek çok iyi üçüncü taraf karşı örneklere sahip değil, sadece çelişkili örnekler. Yorgun.
Ti Strga

Yanıtlar:


1294

Brian Button'dan başka kelimelerle yazılmış:

  1. Genellikle küresel bir örnek olarak kullanılırlar, bu neden bu kadar kötü? Çünkü uygulamanızın bağımlılıklarını kodlarınızda, arayüzler boyunca göstermek yerine gizlersiniz. Etrafından geçmekten kaçınmak için küresel bir şey yapmak bir kod kokusudur .

  2. Tek sorumluluk ilkesini ihlal ediyorlar : kendi yaratılışlarını ve yaşam döngülerini kontrol etmeleri nedeniyle.

  3. Doğal olarak kodun sıkıca birleştirilmesine neden olurlar . Bu, onları test altında taklit etmeyi birçok durumda zorlaştırır.

  4. Uygulamanın ömrü boyunca durumu taşırlar. Testlerin başka bir isabeti, çünkü testlerin sipariş edilmesi gereken bir durumla sonuçlanabilir, bu da birim testler için büyük bir hayır. Neden? Çünkü her birim testi diğerinden bağımsız olmalıdır.


324
Sana katılmıyorum. Yorumlara yalnızca 600 karakter izin verildiğinden, bu konu hakkında yorum yapmak için bir Blog Yazısı yazdım, lütfen aşağıdaki bağlantıya bakın. jorudolph.wordpress.com/2009/11/22/singleton-considerations
Johannes Rudolph

61
Nokta 1 ve 4'te, tek tektonların yararlı olduğunu ve aslında verileri (özellikle bir DB'den) önbelleğe almak için neredeyse mükemmel olduğunu düşünüyorum. Performanstaki artış, birim testlerinin modellenmesinde yer alan karmaşıklıkların çok ötesindedir.
Dai Bok

56
@Dai Bok: "önbellek veri (özellikle bir DB)" bunu yapmak için proxy kalıbı kullanın ...
paxos1977

55
Vay, harika tepkiler. Muhtemelen burada aşırı agresif ifadeler kullandım, bu yüzden lütfen olumsuz bir soruya cevap verdiğimi unutmayın. Cevabım, kötü Singleton kullanımından kaynaklanan 'anti paternlerin' kısa bir listesi olacaktı. Tam açıklama; Ben de zaman zaman Singletons kullanıyorum. SO hakkında, Singletons'un iyi bir fikir olarak değerlendirilebileceği mükemmel forumlar oluşturacak daha tarafsız sorular var. Örneğin, stackoverflow.com/questions/228164/…
Jim Burger

21
Çok iş parçacıklı bir ortamda önbelleğe almak için çok iyi değiller. Sınırlı bir kaynak üzerinden savaşan birden fazla iş parçacığı ile bir önbelleği kolayca yenebilirsiniz. [bir singleton tarafından sürdürülür]
monksy

449

Singletons bir (ve sadece bir) sorunu çözer.

Kaynak Muhafazası.

Eğer bazı kaynaklarınız varsa

( 1 ) yalnızca tek bir örneği olabilir ve

( 2 ) bu tek örneği yönetmeniz gerekir,

bir singletona ihtiyacın var .

Çok fazla örnek yok. Bir günlük dosyası büyük dosyadır. Sadece tek bir günlük dosyasını terk etmek istemezsiniz. Temizlemek, eşitlemek ve düzgün şekilde kapatmak istiyorsunuz. Bu, yönetilmesi gereken tek bir paylaşılan kaynağa örnektir.

Bir singletona ihtiyacınız olması nadirdir. Kötü olmalarının nedeni küresel gibi hissetmeleri ve GoF Tasarım Desenleri kitabının tamamen ücretli bir üyesidir .

Bir küresel ihtiyacınız olduğunu düşündüğünüzde, muhtemelen korkunç bir tasarım hatası yapıyorsunuzdur.


43
Donanım da bir örnek değil mi? Gömülü sistemlerde tekliton veya belki de büyük bir tane kullanabilen birçok donanım bulunur? <sırıtma>
Jeff

170
Tamamen katılıyorum. Uygulamanın yerini alabileceğinin farkında olmadan çok sayıda kararlı "bu uygulama kötü" konuşması var. Genellikle, uygulama sadece "kötü" çünkü sık sık yanlış kullanılır. Uygun şekilde uygulandığı sürece Singleton deseninin doğasında yanlış bir şey yoktur.
Damovisa

53
Prensip olarak, tekil düğmeler çok nadirdir (ve bir yazıcı kuyruğu kesinlikle bir değildir ve bir günlük dosyası değildir - bkz. Log4j). Çoğu zaman, donanım ilke olarak değil tesadüfen tek bir tondur. Bir PC'ye bağlı tüm donanım en iyi ihtimalle tesadüfi bir singleton'dur (birden çok monitör, fare, yazıcı, ses kartı düşünün). 500 milyon $ 'lık bir parçacık detektörü bile tesadüf ve bütçe kısıtlamaları ile tekil bir yazılımdır - bu da yazılımda geçerli değildir, dolayısıyla: singleton yoktur. Telekomünikasyonda, ne telefon numarası ne de fiziksel telefon tekil düğmelerdir (ISDN, çağrı merkezini düşünün).
digitalarbeiter

23
Donanım, çeşitli kaynakların yalnızca bir örneğine sahip olabilir, ancak donanım yazılım değildir. Geliştirme kartındaki tek bir seri bağlantı noktasının Singleton olarak modellenmesi için hiçbir neden yoktur. Aslında, bunu modellemek sadece iki bağlantı noktası olan yeni panoya bağlantıyı zorlaştırır!
dash-tom-bang

19
Yani, iki bağlantı noktasını temsil eden iki tektona sahip olabilmeniz için, aynı kodu çoğaltan iki özdeş sınıf yazacak mısınız? Sadece iki global SerialPort nesnesi kullanmaya ne dersiniz? Donanımı hiç sınıf olarak modellememeye ne dersiniz? Ve neden global erişimi de ima eden singletonları kullanasınız? Kodunuzdaki her işlevin seri bağlantı noktasına erişmesini istiyor musunuz ?
jalf

352

Bazı kodlama zilleri, onlara yüceltilmiş bir küresel olarak bakıyor. Birçok insanın goto ifadesinden nefret ettiği gibi, hiç küresel kullanma fikrinden nefret eden başkaları da vardır . Birkaç geliştiricinin küreselden kaçınmak için olağanüstü uzunluklara gittiğini gördüm çünkü bir tanesini başarısızlığın kabulü olarak kullanmayı düşündüler. Tuhaf ama gerçek.

Uygulamada, Singleton kalıbı sadece kavram araç setinizin yararlı bir parçası olan bir programlama tekniğidir. Zaman zaman ideal bir çözüm bulabilir ve bu yüzden kullanabilirsiniz. Ancak bunu bir tasarım deseni kullanmakla övünmek için kullanmak, onu kullanmayı reddetmek kadar aptalca çünkü sadece küresel .


17
Singleton'da gördüğüm başarısızlık, insanların bir şekilde "daha iyi" oldukları için onları küresel yerine kullanmalarıdır. Sorun (gördüğüm gibi), Singleton'un bu durumlarda masaya getirdiği şeylerin alakasız olmasıdır. (Construct-on-ilk-kullanımda aslında bunu yapmak için yapıcı kullanmıyor olsa bile, örneğin, olmayan-tek uygulamak için Önemsiz.)
tire-tom-bang

21
"Biz" in onlara bakmasının nedeni "biz" in çok sık yanlış ve çok sık kullanıldığını görmektir. "Biz" nedenlerinin yeri olduğunu biliyoruz.
Bjarke Freund-Hansen

5
@Phil, "Zaman zaman bunun ideal çözüm olduğunu görebilir ve bu nedenle kullanabilirsiniz" ifadesini kullandınız. Peki, tam olarak hangi durumda bir singletonu yararlı buluruz?
Pacerier

22
@Pacerier, aşağıdaki koşulların tümü geçerli olduğunda: (1) bir şeyden sadece birine ihtiyacınız vardır, (2) çok sayıda yöntem çağrısında bir şeyi argüman olarak iletmeniz gerekir, (3) lanet şeyi her yerde geçirerek kodunuzun boyutunda ve karmaşıklığında ani bir azalma için bir gün yeniden düzenleme yapma şansı.
antinome

18
Bunun bir şeye cevap verdiğini hissetmiyorum, sadece 'bazen uyuyor, bazen de uymuyor' diyor. Tamam, ama neden ve ne zaman? Bu cevabı ılımlılık argümanından daha fazlası yapan nedir ?
Guildenstern

219

Google'dan Misko Hevery'nin tam olarak bu konuda ilginç makaleleri var ...

Singleton Patolojik Yalancılar , singletonların bağımlılık zincirlerini bulmayı ve bir uygulamayı başlatmayı veya test etmeyi nasıl zorlaştırabileceğini gösteren bir birim test örneğine sahiptir. Kötüye kullanımın oldukça aşırı bir örneğidir, ancak yaptığı nokta hala geçerlidir:

Tekiltonlar küresel devletten başka bir şey değildir. Global durum, nesnelerinizin API'larında bildirilmeyen şeyleri gizlice tutabilmelerini sağlar ve sonuç olarak Singletons, API'larınızı patolojik yalancılara dönüştürür.

Tüm Singletons Gone , bağımlılık enjeksiyonunun, onları gerektiren inşaatçılara örnek almayı kolaylaştırdığı noktasını nereye koyarsa, bu da ilk makalede kötü, küresel Singletons'ın öfkesinin altında yatan ihtiyacı hafifletir.


8
Misko'nun konuyla ilgili makaleleri şu anda üzerinde dolaşan en iyi şey.
Chris Mayer

22
İlk bağlantı aslında teklitonlarla ilgili bir sorunu ele almaz, bunun yerine sınıf içinde statik bir bağımlılık olduğunu varsayar. Verilen örneği parametreleri ileterek düzeltmek mümkündür, ancak yine de tekliton kullanın.
DGM

17
@DGM: Kesinlikle - aslında, makalenin 'mantıklı' kısmı ile 'Tekil nedentir' kısmı arasında büyük bir mantıksal kopukluk var.
Harper Shelby

1
Misko'nun CreditCard makalesi, bu kalıbın yanlış kullanımının aşırı bir örneğidir.
Alex

2
İkinci makalenin okunması Teklitonlar aslında açıklanan nesne modelinin bir parçasıdır. Ancak, küresel olarak erişilebilir olmak yerine bunlar Fabrika nesnelerine özeldir.
FruitBreak

121

Bence karışıklık insanların Singleton modelinin gerçek uygulamasını bilmemelerinden kaynaklanıyor. Bunu yeterince vurgulayamıyorum. Singleton, küreselleri sarmak için bir model değildir . Tekton paterni, belirli bir sınıfın yalnızca bir ve tek bir örneğinin garanti edilmesi için kullanılmalıdır. çalışma süresi boyunca var .

İnsanlar Singleton'un kötülük olduğunu düşünüyor çünkü bunu küreseller için kullanıyorlar. Bu karışıklıktan dolayı Singleton aşağılanır. Lütfen, Singletons ve globalleri karıştırmayın. Amaçlanan amaç için kullanılırsa, Singleton modelinden aşırı faydalar elde edersiniz.


20
Uygulama genelinde mevcut olan tek örnek nedir? Ah. Global . "Singleton globalleri sarmak için bir model değildir " ya tanım ya da yanıltıcıdır, çünkü tanım gereği desen bir sınıfı global bir örnek etrafına sarar.
cHao

26
Elbette uygulamanız başladığında sınıfın bir örneğini oluşturabilir ve bu örneği bir arabirim aracılığıyla onu kullanan herhangi bir şeye enjekte edebilirsiniz. Uygulama sadece bir tane olabileceğine dikkat etmemelidir.
Scott Whitlock

4
@Dainius: Sonunda gerçekten yok. Elbette, örneği keyfi olarak bir başkasıyla değiştiremezsiniz. ( Yaptığınız facepalm uyandırıcı anlar dışında . Daha önce setInstanceyöntemler gördüm .) Bu neredeyse önemli değil - yine de - bir singletona "ihtiyaç duyulan" weenie de kapsülleme veya neyin yanlış olduğu hakkında garip bir şey bilmiyordu değişebilir küresel devlet, bu yüzden (?) her biri için ayarlayıcılar sağladı. tek. alan. (Ve evet, bu gerçekleşir. Çok fazla . Vahşi doğada gördüğüm neredeyse her singleton tasarımla değiştirilebilir ve genellikle utanç verici bir şekilde.)
cHao

4
@Dainius: Büyük ölçüde, zaten var. "Kompozisyonun miras yerine tercih edilmesi" uzun süredir bir şey. Kalıtım zaman olduğu kanıtlanabilir iyi çözüm olsa da, kurs hakkındaki kullanmak serbesttir. Tekiz genel ya, diş ile aynı şey gotovb Bunlar olabilir çalışmak birçok durumda, ama açıkçası, "işler" yeterli değildir - daha iyi nasıl yaklaşımınızı göstermek mümkün olmuştu, sen geleneksel bilgelik karşı gelmek istiyorum olduğu iyi bilinen çözümlere göre. Ve henüz Singleton modeli için böyle bir durum görmedim.
cHao

3
Birbirimizle konuşmayacaksak, sadece küresel olarak mevcut bir örnek hakkında konuşmuyorum. Bunun için birçok dava var. Bahsettiğim şey (özellikle "capital-S Singleton" dediğimde), o tek küresel örneği sınıfın içine gömen, bir getInstanceveya benzer şekilde adlandırılmış bir yöntemle ortaya koyan ve bir ikinci örnek. Açıkçası, bu noktada, tek örneğiniz bile olmayabilir .
cHao

72

Singletonlarla ilgili oldukça kötü bir şey, onları kolayca genişletememenizdir. Temelde bir çeşit dekoratör deseni oluşturmalısınızDavranışlarını değiştirmek isterseniz veya böyle bir şey . Ayrıca, bir gün bu şeyi yapmanın birden fazla yoluna sahip olmak istiyorsanız, kodunuzu nasıl düzenlediğinize bağlı olarak değiştirmek oldukça acı verici olabilir.

Unutulmaması gereken bir şey, eğer singletonları kullanırsanız, doğrudan erişmelerini sağlamak yerine onlara ihtiyaç duyanlara iletmeye çalışın ... Aksi takdirde singleton'un yaptığı şeyi yapmanın birden fazla yolunu seçerseniz, her sınıf, singleton'a doğrudan erişirse bir bağımlılık içerdiğinden değiştirmek oldukça zordur.

Temel olarak:

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

ziyade:

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

Bu tür bir desene bağımlılık enjeksiyonu denildiğine inanıyorum ve genellikle iyi bir şey olarak kabul edilir.

Yine de herhangi bir desen gibi ... Bir düşünün ve verilen durumda kullanımının uygun olup olmadığını düşünün ... Kurallar genellikle kırılmak için yapılır ve desenler düşünülmeden akıllıca uygulanmamalıdır.


17
Heh, bunu her yerde yaparsanız, o zaman her yerde singelton'a bir referans göndermeniz gerekir ve böylece artık bir singletonunuz olmaz. :) (Bu genellikle iyi bir şey IMO'dur.)
Bjarke Freund-Hansen

2
@ BjarkeFreund-Hansen - Saçmalık, neden bahsediyorsun? Bir singleton, bir kez yaratılan bir sınıfın örneğidir. Böyle bir Singleton'a başvurmak asıl nesneyi kopyalamaz, sadece ona referans verir - yine de aynı nesneye sahipsiniz (okuma: Singleton).
M. Mimpen

2
@ M.Mimpen: Hayır, bir sermaye-S Singleton (burada tartışılan şeydir), (a) yalnızca bir örneğin var olacağını garanti eden ve (b) sınıfın kendi aracılığıyla erişilebilen bir sınıf örneğidir. yerleşik küresel erişim noktası. Hiçbir şeyin çağrılmaması gerektiğini beyan ettiyseniz getInstance(), (b) artık tam olarak doğru değildir.
cHao

2
@cHao Seni takip etmiyorum ya da kime yorum yaptığımı anlamıyorsun - ki bu Bjarke Freund-Hansen'e. Bjarke, tekil sonuçlara ilişkin birçok referansa sahip olmanın birkaç tektona sahip olduğunu belirtir. Bu kesinlikle doğru değil çünkü derin kopyalar yok.
M. Mimpen

6
@ M.Mimpen: Semantik etkiye daha fazla değinmek için yorumunu yapıyorum. Çağrıları yasadışı ilan ettikten sonra getInstance(), Singleton paterni ile sıradan referans arasındaki tek yararlı farkı etkili bir şekilde atmışsınızdır. Kodun geri kalanıyla ilgili olarak, bekârlık artık bir mülk değildir. Sadece arayan kişinin getInstance()kaç örnek olduğunu bilmesi veya hatta önemsemesi gerekir. Tek bir arayanla, sınıfın tekilliği güvenilir bir şekilde zorlaması, arayan kişinin sadece bir referans depolaması ve yeniden kullanması için olduğundan daha fazla çaba ve esneklik gerektirir.
cHao

69

Singleton paterni kendi başına bir problem değildir. Sorun, modelin genellikle OO kavramlarını sağlam bir şekilde anlamadan nesne yönelimli araçlarla yazılım geliştiren kişiler tarafından kullanılmasıdır. Bu bağlamda singletonlar sunulduğunda, her küçük kullanım için yardımcı yöntemler içeren yönetilemez sınıflara dönüşme eğilimindedirler.

Singletons da test açısından bir sorundur. İzole ünite testlerinin yazılmasını zorlaştırırlar. Kontrolün tersine çevrilmesi (IoC) ve bağımlılık enjeksiyonu , bu problemi, birim testine katkıda bulunan nesne yönelimli bir şekilde aşmayı amaçlayan kalıplardır.

Bir de çöp toplanan çevre singletons hızla bellek yönetimi konusunda bir sorun haline gelebilir.

Teklitonların bir darboğaz ve bir senkronizasyon sorunu olabileceği çok iş parçacıklı senaryo da vardır.


4
çok yaşındaki bir iplik olduğunu biliyorum. merhaba @ Kimoz u dedi: - singletons hızla bellek yönetimi ile ilgili bir sorun haline gelebilir. singleton ve çöp toplama konusunda ne tür bir sorunun gelebileceğini daha ayrıntılı olarak açıklamak isteriz.
Thomas

@Kimoz, Soru " Tekli kalıp neden kendi başına bir sorun değil?" ve sadece noktayı tekrarladınız, ancak singleton deseninin geçerli tek bir kullanım durumunu bile sağlamadınız.
Pacerier

@Thomas, çünkü tek bir örnekte tanım gereği tekil bir tane var. Bu nedenle, null değerine tek başvuruyu atamak genellikle karmaşıktır. Bu yapılabilir, ancak bu, singletonun uygulamanızda kullanılmadığı noktayı tam olarak kontrol ettiğiniz anlamına gelir. Ve bu nadirdir ve genellikle singleton tutkunlarının aradığı şeylerin tam tersidir: tek bir örneği her zaman erişilebilir hale getirmenin basit bir yolu . Guice veya Dagger gibi bazı DI çerçevelerinde, bir singletondan kurtulmak mümkün değildir ve sonsuza kadar bellekte kalır. (Her ne kadar konteyner sağlanan singleton ev yapımı olanlardan çok daha iyidir).
Snicolas

54

Tek bir statik yöntem kullanılarak uygulanır. Ünite testi yapan kişiler, alay edilemeyecekleri veya inatlanamayacakları için statik yöntemlerden kaçınırlar. Bu sitedeki çoğu kişi, birim testlerin büyük savunucularıdır. Bunlardan kaçınmak için genellikle en kabul edilen kural , kontrol paterninin tersini kullanmaktır .


9
Bu, nesneleri (birimleri), işlevleri (birimleri), tüm kitaplıkları (birimleri) test edebilen, ancak sınıftaki statik herhangi bir şeyle (ayrıca birimler) başarısız olan birim testinde bir soruna benziyor.
v010dya

2
Zaten tüm dış referansları moc'a atmayı düşünmüyor musun? Eğer öyleyse, o zaman moc singleton için bir sorun var, değilse, gerçekten birim test yapıyor musunuz?
Dainius

@Dainius: Alay örnekleri , alay sınıflarından çok daha az sorun. Muhtemelen test edilen sınıfı uygulamanın geri kalanından çıkarabilir ve sahte bir Singletonsınıfla test edebilirsiniz . Ancak, bu test sürecini son derece karmaşıklaştırır. Birincisi, şimdi sınıfları irade dışı bırakabilmeniz (çoğu dilde gerçekten bir seçenek değil) veya her test için yeni bir VM başlatabilmeniz gerekir (okuma: testler binlerce kez sürebilir). Bununla birlikte, iki kişi için bağımlılık Singleton, şimdi tüm testlerinize sızan bir uygulama detayıdır.
cHao

Powermock statik şeylerle alay edebilir.
Snicolas

bir nesneyi alay etmek gerçek nesne yaratmak anlamına mı gelir? Mocking gerçek bir nesne oluşturmazsa, neden sınıfın singleton veya yöntemin statik olması önemli olur?
Arun Raaj

45

Tekli kümelenme söz konusu olduğunda da kötüdür . Çünkü o zaman, artık uygulamanızda "tam olarak bir singleton" yok.

Aşağıdaki durumu göz önünde bulundurun: Bir geliştirici olarak, veritabanına erişen bir web uygulaması oluşturmanız gerekir. Eşzamanlı veritabanı çağrılarının birbiriyle çakışmadığından emin olmak için bir iş parçacığı kaydetme oluşturursunuz SingletonDao:

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

Dolayısıyla, uygulamanızda yalnızca bir singleton bulunduğundan ve tüm veritabanının sadece bir tanesinden geçtiğinden emin olabilirsiniz SingletonDao. Üretim ortamınız şu şekilde görünüyor: Tek Singleton

Şimdiye kadar her şey yolunda.

Şimdi, bir kümede web uygulamanızın birden çok örneğini ayarlamak istediğinizi düşünün. Şimdi, aniden böyle bir şey var:

Birçok singleton

Kulağa tuhaf geliyor, ama şimdi uygulamanızda birçok singleton var . Ve bu bir singletonun tam olarak olması gerekmiyor: Bunun birçok nesnesine sahip olmak. Bu örnekte gösterildiği gibi bir veritabanına senkronize çağrı yapmak istiyorsanız bu özellikle kötüdür.

Tabii ki bu bir singletonun kötü kullanımına bir örnektir. Ancak bu örneğin mesajı şöyledir: Uygulamanızda tam olarak tek bir örnek bulunduğuna güvenemezsiniz - özellikle de kümeleme söz konusu olduğunda.


4
Eğer singleton'u nasıl uygulayacağınızı bilmiyorsanız, bunu yapmamalısınız. Ne yaptığınızı bilmiyorsanız, önce bunu bulmalısınız ve ancak bundan sonra ihtiyacınız olanı yapın.
Dainius

3
Bu ilginç, bir sorum var. Peki tek tekler - her biri farklı bir makinede / JVM'de - tek bir veritabanına bağlanıyorsa sorun tam olarak nedir? Singletons kapsamı yalnızca belirli JVM içindir ve bu bir kümede bile geçerlidir. Felsefi olarak bu özel durumun kötü olduğunu söylemekten ziyade, amacımız uygulama boyunca tek bir nesne olduğu için, bu düzenleme nedeniyle ortaya çıkabilecek herhangi bir teknik problemi görmekten memnuniyet duyarım.
Saurabh Patil

39
  1. Kolayca (ab) bir global değişken olarak kullanılır.
  2. Tekil tonlara bağımlı sınıflar, birim testten ayrı olarak nispeten daha zordur.

33

Tekel şeytandır ve salt okunur / değişmez duruma sahip singletonlar 'gerçek' problemdir ...

Singletonları okuduktan sonra , Jason cevabında önerildiği gibi Patolojik Yalancılar , singletonların sıklıkla nasıl yanlış kullanıldığına dair en iyi sunulmuş örneği sunan bu küçük tidbit ile karşılaştım .

Global kötü çünkü:

  • a. Ad alanı çakışmasına neden olur
  • b. Devleti haksız bir şekilde ortaya çıkarır

Singletons söz konusu olduğunda

  • a. Onları çağırmanın açık OO yolu çatışmaları önler, bu yüzden a noktası. sorun değil
  • b. Durumu olmayan singletonlar (fabrikalar gibi) sorun değildir. Durumlu tektonlar, tekrar değiştirilemeyen veya bir kez yazılan ve çok sayıda (config / özellik dosyaları) olanlar olmak üzere iki kategoriye ayrılabilir. Bunlar fena değil. Bir tür referans sahibi olan Mutable Singletons, bahsettiğiniz kişilerdir.

Son açıklamada blogun 'singleton liar are' kavramına atıfta bulunuyor.

Bu Tekel için nasıl geçerlidir?

Tekel oyununa başlamak için önce:

  • önce kuralları belirleriz, böylece herkes aynı sayfada olur
  • Oyunun başında herkese eşit bir başlangıç ​​verilir
  • karışıklığı önlemek için sadece bir kurallar dizisi sunulur
  • kuralların oyun boyunca değişmesine izin verilmiyor

Şimdi, gerçekten tekel oynamamış olan herkes için , bu standartlar en iyi ihtimalle idealdir. Tekel yenilgisinin yutulması zordur, çünkü tekel parayla ilgilidir, eğer kaybederseniz, oyuncuların geri kalanının oyunu bitirmesini dikkatle izlemelisiniz ve kayıplar genellikle hızlı ve ezicidir. Bu nedenle, kurallar genellikle bir noktada bazı oyuncuların kendi çıkarlarına, diğerlerinin pahasına hizmet etmek için bükülür.

Yani arkadaşların Bob, Joe ve Ed ile tekel oynuyorsun. İmparatorluğunuzu hızla inşa ediyorsunuz ve pazar payını katlanarak artırıyorsunuz. Rakipleriniz zayıflıyor ve kan kokusu almaya başlıyorsunuz (mecazi olarak). Arkadaţýn Bob tüm parasýný olabildiđince düţük deđerli mülklere yönlendiriyordu, ancak beklediđi gibi yüksek bir yatırım getirisi almýyor. Bob, kötü şansın inişi olarak Boardwalk'ınıza iner ve oyundan çıkarılır.

Şimdi oyun kolay zar atma ciddi iş gidiyor. Bob başarısızlığa örnek oldu ve Joe ve Ed 'o adam' gibi olmak istemiyorlar. Böylece, önde gelen oyuncu olarak, birdenbire düşman olursunuz. Joe ve Ed, masa altı esnafları, arkadan para enjeksiyonları, değersiz ev değiştirme ve genellikle bir oyuncu zirveye çıkana kadar sizi zayıflatmak için her şeyi yapmaya başlarlar.

Sonra, bunlardan biri kazanmak yerine, süreç baştan başlar. Birdenbire, sonlu bir kurallar dizisi hareketli bir hedef haline gelir ve oyun Survivor'dan bu yana her yüksek puanlı gerçeklik TV şovunun temelini oluşturacak sosyal etkileşimlere dönüşür. Neden, çünkü kurallar değişiyor ve nasıl / neden / neyi temsil etmeleri gerektiği konusunda fikir birliği yok ve daha da önemlisi, kararları veren hiç kimse yok. Bu noktada oyundaki her oyuncu kendi kurallarını yapıyor ve kaos, iki oyuncu yoldaşlığı sürdürmek ve yavaşça vazgeçmek için çok yorgun olana kadar devam ediyor.

Dolayısıyla, bir oyunun kural kitabı tek birtonu doğru bir şekilde temsil ederse, tekel kural kitabı istismarın bir örneği olacaktır.

Bu programlama için nasıl geçerlidir?

Değişken tek tonların ortaya çıkardığı açık iş parçacığı güvenliği ve senkronizasyon sorunlarının yanı sıra ... Birden fazla farklı kaynak tarafından aynı anda okunabilen / değiştirilebilen ve uygulama yürütme süresi boyunca mevcut olan bir veri kümeniz varsa, muhtemelen geri adım atmak ve "burada doğru veri yapısını kullanıyor muyum?"

Şahsen, ben bir programcı bir uygulama içinde bir tür bükülmüş çapraz iplik veritabanı mağaza olarak kullanarak bir singleton kötüye gördük. Doğrudan kod üzerinde çalıştıktan sonra, yavaş bir şekilde (iş parçacığı güvenli hale getirmek için gerekli tüm iplik kilitleri nedeniyle) ve üzerinde çalışmak için bir kabus olduğunu (senkronizasyon hatalarının öngörülemeyen / aralıklı doğası nedeniyle) ve 'üretim' koşullarında test etmek neredeyse imkansız. Elbette, bazı performans sorunlarının üstesinden gelmek için yoklama / sinyalleme kullanılarak bir sistem geliştirilebilirdi, ancak bu testlerle ilgili sorunları çözmez ve neden bir 'gerçek' veritabanı zaten aynı işlevselliği çok daha sağlam bir şekilde başarabilirse rahatsız olur? / ölçeklenebilir şekilde.

Bir Singleton yalnızca bir singleton'un sağladığı şeye ihtiyacınız varsa bir seçenektir. Bir nesnenin salt okunur bir örneği. Aynı kural, nesnenin özelliklerine / üyelerine de basamaklanmalıdır.


1
Singleton bazı verileri bozarsa ne olur?
Yola

@Yola Singleton önceden belirlenmiş ve / veya sabit bir programda güncellenmesi planlanmışsa, böylece yazışma sayısını azaltacaksa yazma sayısını azaltacaktır. Yine de, aynı kullanımı simüle eden tekil olmayan bir örneği taklit etmedikçe, singleton ile etkileşime giren kodların hiçbirini doğru bir şekilde test edemezsiniz. TDD millet muhtemelen uygun olurdu ama işe yarayacaktı.
Evan Plaice

@EvanPlaice: Bir alayla bile olsa, kodla ilgili bazı sorunlarınız olur Singleton.getInstance(). Yansımayı destekleyen bir dil, Tek Gerçek Eşgörünüm'ün depolandığı alanı ayarlayarak bu soruna geçici bir çözüm bulabilir. Yine de IMO, başka bir sınıfın özel durumu ile maymunlaşmaya başladığınızda testler biraz daha az güvenilir hale gelir.
cHao

28

Singleton tek bir örnek değildir!

Diğer cevaplardan farklı olarak, Singletons ile neyin yanlış olduğu hakkında konuşmak istemiyorum, ancak doğru kullanıldıklarında ne kadar güçlü ve harika olduklarını göstermek istiyorum!

  • Sorun : Singleton, çoklu iş parçacığı oluşturma ortamında bir sorun olabilir
    Çözüm : Singletonunuzun tüm bağımlılıklarını başlatmak için tek iş parçacıklı bir önyükleme işlemi kullanın.
  • Sorun : Tektonlarla alay etmek zor.
    Çözüm : Alay için Fabrika şablonunu kullanın

Sen haritalayabilirsiniz MyModeliçin TestMyModelzaman devralır, her yerde bu sınıfın MyModelenjekte edilecek alırsınız TestMyModelinstread. - Sorun : Singletonlar hiçbir zaman atılmadıkları için bellek sızıntılarına neden olabilir.
Çözüm : Peki, onları atın! Tek tektonları düzgün bir şekilde atmak için uygulamanızda geri arama yapın, bunlara bağlı tüm verileri kaldırmanız ve son olarak Fabrikadan kaldırmanız gerekir.

Başlıkta da belirttiğim gibi singleton tek örnekle ilgili değil.

  • Singletons okunabilirliği artırır : Sınıfınıza bakabilir ve bağımlılığın ne olduğunu anlamak için hangi singletonun enjekte edildiğini görebilirsiniz.
  • Singletons bakımı geliştirir : Bir sınıftan bir bağımlılığı kaldırdığınızda, sadece bir singleton enjeksiyonunu sildiniz, sadece bağımlılığınızı hareket ettiren diğer sınıfların büyük bir bağlantısını düzenlemenize gerek yoktur (Bu benim için kötü kokulu bir kod @Jim Burger )
  • Singletons belleği ve performansı geliştirir : Uygulamanızda bir şey olduğunda ve iletilmesi uzun bir geri arama zinciri gerektirdiğinde, ortadaki adamı kesip, performans ve bellek kullanımınızı iyileştirdiğiniz Singleton'u kullanarak bellek ve performans israfına uğramış olursunuz ( gereksiz yerel değişken tahsislerinden kaçınarak).

2
Bu, Singletons'la ilgili asıl sorunumu ele almıyor, bu da projenizdeki binlerce sınıftan herhangi birinden küresel duruma erişime izin veriyor.
LegendLength

8
Amaç bu ...
Ilya Gazman

LegendLength Bu neden yanlış? Örneğin, uygulamamda Settings, her widget'ın görüntülenen sayıların nasıl biçimlendirileceğini bilmesi için herhangi bir widget'tan erişilebilen tek bir nesnem var . Küresel olarak erişilebilir bir nesne olmasaydı, onu yapıcıdaki her bir widget'a yapıcıya enjekte etmeli ve ona referansı bir değişken olarak tutmalıydım. Bu korkunç bir hafıza ve zaman kaybıdır.
VK

23

Singletons'un ne kadar kötü olduğuna dair cevabım daima “doğru yapmak zordur”. Dillerin temel bileşenlerinin çoğu, hesaplamanın diğer yönlerindeki bileşenler (localhost, varsayılan yol, sanal dosya sistemi, vb.) Gibi tek başlı düğmelerdir (sınıflar, işlevler, ad alanları ve hatta işleçler) ve tesadüfen değildir. Zaman zaman sorun ve hayal kırıklığına neden olurlarsa da, birçok şeyin çok daha iyi çalışmasını sağlayabilirler.

Gördüğüm en büyük iki vida: küresel gibi davranmak ve Singleton kapanışını tanımlayamamak.

Herkes Singleton'ın küresel olarak konuşur, çünkü temelde öyle. Bununla birlikte, bir küreseldeki kötülüğün büyük bir kısmı (ne yazık ki, hepsi değil), küresel olarak küresel olmaktan değil, onu nasıl kullandığınızdan gelir. Singletons için de aynı şey geçerli. Aslında daha çok "tek örnek" gerçekten "küresel olarak erişilebilir" anlamına gelmez. Bu daha doğal bir yan üründür ve ondan geldiğini bildiğimiz tüm kötüler göz önüne alındığında, küresel erişilebilirliği kullanmak için acele etmemeliyiz. Programcılar bir Singleton gördükten sonra her zaman doğrudan kendi örnek yöntemi aracılığıyla erişiyor gibi görünüyorlar. Bunun yerine, tıpkı diğer herhangi bir nesne gibi ona gitmelisiniz. Çoğu kod bir Singleton ile uğraştığının farkında bile olmamalı (gevşek bağlantı, değil mi?). Yalnızca küçük bir kod nesneye global gibi erişirse, çok fazla zarar geri alınır.

Singleton bağlamı da gerçekten önemlidir. Bir Singleton'un belirleyici özelliği "sadece bir" olduğudur, ama gerçek şu ki bir tür bağlam / ad alanı içinde "sadece bir" dir. Genellikle aşağıdakilerden biridir: iş parçacığı, işlem, IP adresi veya küme başına bir tane, ancak aynı zamanda işlemci, makine, dil ad alanı / sınıf yükleyici / her neyse, alt ağ, İnternet vb.

Diğer, daha az yaygın olan hata, Singleton yaşam tarzını görmezden gelmektir. Sadece bir tane olması, bir Singleton'un her zaman her zaman olduğu ve her zaman olacağı anlamına gelmediği anlamına gelmez, ne de genellikle arzu edilir değildir (başlangıç ​​ve sonu olmayan nesneler koddaki her türlü yararlı varsayımı ihlal eder ve sadece kullanılmalıdır. en çaresiz koşullarda.

Bu hatalardan kaçınırsanız, Singletons hala bir PITA olabilir, en kötü sorunların birçoğunun önemli ölçüde azaldığını görmeye hazırsınız. Tanımlanmış oluşturma ve imha yöntemleri ve ne zaman ve nasıl çağrıldıklarını belirten ve "örnek" yöntemi olan bir yaşam döngüsü ile, açıkça bir sınıf yükleyici başına bir kez (yani bir iş parçacığı güvenlik politikasına ihtiyaç duyduğu anlamına gelir) olarak tanımlanan bir Java Singleton düşünün genellikle diğer küresel olmayan nesneler aracılığıyla erişilebilir. Hala potansiyel bir sorun kaynağı, ama kesinlikle çok daha az sorun.

Ne yazık ki, Singletons'un nasıl yapılacağına dair iyi örnekler vermek yerine. Kötü örnekler öğretiriz, programcıların bir süreliğine bunları kullanarak kaçmasına izin veririz ve sonra onlara kötü bir tasarım deseni olduklarını söyleriz.


23

Wikipedia'ya Bak Singleton_pattern

Ayrıca, aşırı kullanıldığını düşünen ve sadece bir sınıf örneğinin gerçekten gerekli olmadığı durumlarda gereksiz sınırlamalar getiren bazı insanlar tarafından bir anti-desen olarak kabul edilir. [1] [2] [3] [4]

Kaynaklar (sadece makaleden ilgili referanslar)

  1. ^ Alex Miller. Nefret ettiğim kalıplar # 1: Singleton , Temmuz 2007
  2. ^ Scott Densmore. Singletons neden kötüdür , Mayıs 2004
  3. ^ Steve Yegge. Singletons aptal sayıldı , Eylül 2004
  4. ^ JB Rainsberger, IBM. Singletonlarınızı akıllıca kullanın , Temmuz 2001

8
Desen hakkında bir açıklama neden kötü olarak kabul edildiğini
açıklamıyor

2
Oldukça adil: "Ayrıca, aşırı kullanıldığını düşünen ve sadece bir sınıf örneğinin gerçekten gerekli olmadığı durumlarda gereksiz sınırlamalar getiren bazı insanlar tarafından bir anti-desen olarak kabul edilir." Referanslara bakın ... Her neyse benim için RTFM var.
GUI Junkie

17

Singletonların kendileri kötü değil, ama GoF tasarım deseni kötü. Geçerli olan tek gerçek argüman, özellikle testler paralel olarak yapılıyorsa, GoF tasarım modelinin test açısından kendini ödünç vermemesidir.

Bir sınıfın tek bir örneğini kullanmak, kodda aşağıdaki yöntemleri uyguladığınız sürece geçerli bir yapıdır:

  1. Tekton olarak kullanılacak sınıfın bir arabirim uyguladığından emin olun. Bu, aynı arayüz kullanılarak taslakların veya alayların uygulanmasına izin verir

  2. Singleton'un iş parçacığı açısından güvenli olduğundan emin olun. Bu belli.

  3. Singleton doğada basit olmalı ve aşırı derecede karmaşık olmamalıdır.

  4. Tek bir nesnenin belirli bir nesneye aktarılması gereken uygulamanızın çalışma zamanı sırasında, bu nesneyi oluşturan bir sınıf fabrikası kullanın ve sınıf fabrikasının tekli örneği ihtiyaç duyduğu sınıfa geçirmesini sağlayın.

  5. Test sırasında ve deterministik davranışı sağlamak için, singleton sınıfını gerçek sınıfın kendisi veya davranışını uygulayan bir saplama / alay olarak ayrı bir örnek olarak oluşturun ve bunu gerektiren sınıfa olduğu gibi iletin. Test sırasında singletona ihtiyaç duyan test edilen nesneyi oluşturan sınıf faktörünü kullanmayın, çünkü amacı tek başına global bir örneğini geçecektir.

Çözümlerimizde paralel test çalışma akışlarında deterministik davranışı sağlayan test edilebilir büyük bir başarı ile Singletons kullandık.


+1, Son olarak bir singletonun ne zaman geçerli olabileceğini gösteren bir cevap .
Pacerier

16

Kabul edilen cevaptaki 4 noktaya değinmek istiyorum, umarım birisi neden yanlış olduğumu açıklayabilir.

  1. Kodunuzdaki bağımlılıkları gizlemek neden kötü? Zaten düzinelerce gizli bağımlılık var (C çalışma zamanı çağrıları, OS API çağrıları, genel işlev çağrıları) ve tekil bağımlılıklar bulmak kolaydır (örneğin ara ()).

    "Etrafından kaçınmak için küresel bir şey yapmak bir kod kokusudur." Neden tek bir kod kokusu yapmaktan kaçınmak için bir şey geçirmiyorsunuz?

    Bir nesneyi sadece bir tektondan kaçınmak için bir çağrı yığınındaki 10 işlevden geçiriyorsanız, bu harika mi?

  2. Tek Sorumluluk İlke: Bence bu biraz belirsiz ve sorumluluk tanımınıza bağlı. İlgili bir soru, bir sınıfa bu özel "sorumluluğu" eklemek neden önemlidir?

  3. Neden bir nesneyi sınıfa aktarmak, nesneyi sınıf içinde tekil olarak kullanmaktan daha sıkı bir şekilde birbirine bağlar?

  4. Devletin ömrünü neden değiştiriyor? Teklitonlar manuel olarak oluşturulabilir veya yok edilebilir, böylece kontrol hala oradadır ve yaşam süresini tekil olmayan bir nesnenin ömrü ile aynı hale getirebilirsiniz.

Birim testleri ile ilgili:

  • tüm sınıfların birim teste tabi tutulması gerekmez
  • birim test edilmesi gereken tüm sınıfların singletonun uygulanmasını değiştirmesi gerekmez
  • Onlar eğer yapmak birim test edilmiş olmalı ve uygulamasını değiştirme gereğini yapmak gerek, bu bağımlılık enjeksiyon yoluyla kendisine geçirilen singleton'ununu sahip a tek kullanmasını bir sınıf değiştirmek kolaydır.

1
1. Bütün bu gizli bağımlılıklar? Bunlar da kötü. Gizli bağımlılıklar her zaman kötüdür. Ama CRT ve OS durumunda, zaten oradalar ve bir şey yapmanın tek yolu onlar. (Çalışma zamanını veya işletim sistemini kullanmadan bir C programı yazmayı deneyin.) Bu şeyleri yapma yeteneği olumsuzluklarından büyük ölçüde ağır basar. Kodumuzdaki singletonlar bu lüksü alamıyor; kontrol ve sorumluluk alanımıza sıkı sıkıya bağlı oldukları için, her kullanım (okuma: her ek gizli bağımlılık), işleri yapmanın tek makul yolu olarak haklı olmalıdır. Çok, çok azı aslında.
cHao

2. "Sorumluluk" tipik olarak "değişim nedeni" olarak tanımlanır. Singleton hem ömrünü yönetir hem de gerçek işini yapar. İşin nasıl yapıldığını değiştirirseniz veya nesne grafiğinin nasıl oluşturulduğunu değiştirirseniz, sınıfı değiştirmeniz gerekir. Ancak, nesne grafiğini oluşturmak için başka bir kodunuz varsa ve sınıfınız gerçek işini yaparsa, bu başlatma kodu nesne grafiğini istediği gibi ayarlayabilir. Her şey çok daha modüler ve test edilebilir, çünkü test çiftlerini ekleyebilir ve her şeyi dilediğiniz zaman yırtabilirsiniz ve artık gizli hareketli parçalara güvenmiyorsunuz.
cHao

1
@cHao: 1. "Bir şeyler yapma yeteneğinin olumsuzluklarından daha ağır bastığını" fark etmeniz iyi olur. Bir günlük kaydı çerçevesi buna örnek olarak gösterilebilir. Geliştiricilerin günlüğe kaydetmekten vazgeçmeleri anlamına gelirse, işlev günlüğü katmanlarına bağımlılık enjeksiyonu yoluyla küresel bir günlük nesnesini geçirmeyi zorunlu kılar. Dolayısıyla, singleton. 3. Sıkı bağlantı noktanız sadece sınıfın müşterilerinin polimorfizm yoluyla davranışı kontrol etmesini istiyorsanız önemlidir. Genellikle durum böyle değildir.
Jon

2
4. "Manüel olarak imha edilemez" in "yalnızca bir örnek olabilir" in mantıklı bir anlamı olduğundan emin değilim. Eğer singleton'ı bu şekilde tanımlıyorsanız, aynı şeyden bahsetmiyoruz. Tüm sınıflar birim test edilmemelidir . Bu bir iş kararıdır ve bazen pazara sunma süresi veya geliştirme maliyeti öncelikli olur.
Jon

1
3. Eğer polimorfizm istemiyorsanız, bir örneğe bile ihtiyacınız yoktur. Bunu statik bir sınıf haline getirebilirsiniz, çünkü kendinizi yine de tek bir nesneye ve tek bir uygulamaya bağlıyorsunuz - ve en azından API size bu kadar yalan söylemiyor.
cHao

15

Vince Huston benim için makul görünen şu kriterlere sahip:

Singleton, yalnızca aşağıdaki kriterlerin üçü de karşılandığında dikkate alınmalıdır:

  • Tek bir örneğin mülkiyeti makul şekilde atanamaz
  • Tembel başlatma arzu edilir
  • Global erişim başka türlü sağlanmamaktadır

Tek bir örneğin mülkiyeti, ne zaman ve nasıl başlatıldığı ve global erişim sorun değilse, Singleton yeterince ilginç değildir.


14

Singleton, saf bir bakış açısından kötüdür.

Pratik açıdan bakıldığında, singleton karmaşıklığa karşı bir değiş tokuş gelişme zamanıdır .

Uygulamanız bu kadar değişmez biliyorsanız, gitmek için oldukça iyi. Gereksinimleriniz beklenmedik bir şekilde değişirse (çoğu durumda oldukça iyi) işleri yeniden düzenlemeniz gerekebilir.

Singletons bazen birim testini de zorlaştırır .


3
Deneyimlerim, singletonlarla başlamanın , kısa vadede bile, uzun vadeyi saymadan gerçekten size zarar vereceğidir. Bu etki daha az olabilir, bu yüzden uygulama zaten bir süredir mevcutsa ve muhtemelen diğer tekil düğmelerle istila edilmişse. En baştan başlamak? Onları pahasına önlemek! Bir nesnenin tek bir örneğini mi istiyorsunuz? Bırakın bir fabrika bu nesnenin örneğini yönetsin.
Sven

1
AFAIK Singleton ise bir fabrika yöntemi. Bu yüzden öneri almıyorum.
takon

3
"A factory"! = "Aynı sınıftaki diğer faydalı şeylerden ayrılamayan bir factory_method"
Sven

13

Modelinizin gerçekte tek olan bazı yönleri için kullanıldığı varsayılarak, desende doğal olarak yanlış bir şey yoktur.

Geri tepmenin aşırı kullanımdan kaynaklandığına inanıyorum, bu da anlaşılması ve uygulanması en kolay örüntü olmasından kaynaklanıyor.


6
Bence tepkinin resmi bir birim testi uygulayan insanlar ile başa çıkmak için bir kabus olduklarını fark etmeleri daha önemli.
Jim Burger

1
Bunun neden -1 olduğundan emin değilim. En açıklayıcı cevap değil ama o da yanlış değil.
Yasadışı Programcı

13

İyi / kötü argüman hakkında yorum yapmayacağım, ama Bahar geldiğinden beri bunları kullanmadım . Bağımlılık enjeksiyonu kullanmak , singleton, servicelocators ve fabrikalar için gereksinimlerimi neredeyse kaldırdı. Bunu en azından yaptığım iş için (Java tabanlı web uygulamaları) çok daha üretken ve temiz bir ortam olarak görüyorum.


3
Bahar fasulyesi, varsayılan olarak, Singletons değil mi?
ince

3
Evet, ama tek tek 'kodlama' demek istiyorum. 'Singleton' yazmadım (yani tasarım modeline göre özel bir kurucu yada yada yada ile) - ama evet, bahar ile tek bir örnek kullanıyorum.
prime

4
öyleyse diğerleri yaptığında, sorun yok (eğer bunu sonra kullanacaksam), ama diğerleri yaparsa ve kullanmayacağım, şeytani mi?
Dainius

11

Singleton bir modeldir ve diğer araçlar gibi kullanılabilir veya kötüye kullanılabilir.

Bir singletonun kötü kısmı genellikle kullanıcıdır (ya da bir singletonun yapmak için tasarlanmadığı şeyler için uygunsuz kullanımını söylemeliyim). En büyük suçlu, singleton'u sahte bir global değişken olarak kullanmaktır.


1
Teşekkürler Loki :) Kesinlikle benim açımdan. Bütün cadı avı bana tartışma tartışmasını hatırlatıyor. Araçlar, bunları nasıl kullanacaklarını bilen insanlar içindir; hoşunuza gitmeyen bir araç kullanmak sizin için tehlikeli olabilir, ancak bundan kaçının, başkalarına, doğru bir şekilde kullanmayı öğrenmemelerini / kullanmamalarını söylemeyin. Tam olarak "pro-singleton" değilim ama böyle bir aracım olduğu gerçeğini seviyorum ve onu kullanmanın uygun olduğu yerde hissediyorum. Dönemi.
dkellner

8

Bir logger veya bir veritabanı bağlantısı gibi tektonlar kullanarak kod yazdığınızda ve daha sonra birden fazla günlüğe veya birden fazla veritabanına ihtiyacınız olduğunu fark ederseniz, sorun yaşarsınız.

Singletons, onlardan normal nesnelere taşınmayı çok zorlaştırır.

Ayrıca, iş parçacığı için güvenli olmayan bir singleton yazmak çok kolaydır.

Tektonları kullanmak yerine, gerekli tüm yardımcı nesneleri işlevden işleve geçirmelisiniz. Hepsini bir yardımcı nesneye sarırsanız, bu basitleştirilebilir:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

4
her yöntem için argüman geçirmek mükemmel, api'nizi kirletmenin en az 10 yoluna gitmeli.
Dainius

Bu bariz bir dezavantaj ve dil tarafından ele alınması gereken bir şey, bir şekilde iç içe çağrılara yayılan argümanlar aracılığıyla, ancak böyle bir destek yoksa, elle yapılması gerekiyor. Sistem saatini temsil eden bir nesne gibi doğal tektonların bile sorun potansiyeli olduğunu düşünün. Örneğin, saate bağlı kodu (çoklu tarife elektrik sayacını düşünün) testlerle nasıl kaplayacaksınız?
Roman Odaisky

ne test edeceğinize bağlıdır, eğer singleton test etmiyorsanız, nasıl davrandığınızı önemsiyorsunuz, eğer 2 uzak nesneniz birbirinin davranışına bağlıysa, bu singleton problemi değildir ..
Dainius

Hangi dilin iç içe çağrılara yayılmasını sağlayabilir misiniz?
Dainius

1
Bir diğerine bağlı olan bir modülünüz varsa ve bunu bilmiyor / mocunu yapamıyorsanız, tek başına olup olmadığına bakılmaksızın çok zorlanacaksınız. İnsanlar kalıtım yöntemlerini sık sık, kompozisyon kullanmaları gereken yerlerde kullanıyorlar, ancak kalıtımın kötü olduğunu ve OOP'un her ne pahasına olursa olsun kaçınması gerektiğini söylemiyorsunuz, çünkü birçok insan orada tasarım hataları yapıyor mu?
Dainius

8

Tektonlarla ilgili sorunlar, daha fazla kapsam ve dolayısıyla birleştirme konusudur . Tek bir örneğe erişmeniz gereken bazı durumlar olduğunu inkar etmek mümkün değildir ve başka yollarla da gerçekleştirilebilir.

Şimdi bir kontrol (IoC) konteyneri tersine çevrmeyi tasarlamayı ve ömürlerin konteyner tarafından kontrol edilmesini sağlamayı tercih ediyorum . Bu, tek bir örnek olduğu gerçeğinden habersiz olmanız için örneğe bağlı olan sınıfların faydasını sağlar. Singletonun ömrü gelecekte değiştirilebilir. Son zamanlarda karşılaştığım böyle bir örnek, tek iş parçacıktan çoklu iş parçacığına kadar kolay bir ayarlama oldu.

FWIW, eğer birimi test etmeye çalıştığınızda bir PIA ise, hata ayıklamaya, hata düzeltmeye veya geliştirmeye çalıştığınızda PIA'ya gidecektir.



7

Singletons kötü DEĞİLDİR. Sadece global olarak benzersiz olmayan, global olarak benzersiz olmayan bir şey yaptığınızda kötüdür.

Ancak, "uygulama kapsam hizmetleri" vardır (bileşenleri etkileşimli kılan bir mesajlaşma sistemini düşünün) - bu tek birton için CALLS, bir "MessageQueue" - "" SendMessage (...) "yöntemi olan bir sınıf.

Daha sonra her yerden aşağıdakileri yapabilirsiniz:

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

Ve tabii ki şunları yapın:

MessageQueue.Current.RegisterReceiver (bu);

IMessageReceiver uygulayan sınıflarda.


6
Ve daha küçük bir kapsamla ikinci bir mesaj kuyruğu oluşturmak istersem neden kodunuzu oluşturmak için yeniden kullanmama izin verilmiyor? Singleton bunu engeller. Ancak, normal bir ileti kuyruğu sınıfı oluşturduysanız ve daha sonra "uygulama kapsamı" olarak davranacak bir genel örnek oluşturduysanız, diğer kullanım için ikinci bir örnek oluşturabilirim. Ancak sınıfı bir singleton yaparsanız, ikinci bir mesaj kuyruğu sınıfı yazmam gerekir.
jalf

1
Ayrıca neden MessageQueue gibi herhangi bir yerden güzelce çağrılabilmemiz için neden sadece global bir CustomerOrderList'e sahip olmamalıyız? Cevabın her ikisi için de aynı olduğuna inanıyorum: Etkili bir küresel değişken yapıyor.
LegendLength

7

Çok fazla kişi tek bir modelde iş parçacığı için güvenli olmayan nesneleri koyar. DataContext iş parçacığı güvenli ve tamamen bir iş birimi nesnesi olmasına rağmen, tek bir desende yapılan bir DataContext ( LINQ to SQL ) örnekleri gördüm .


birçok kişi güvensiz çok iş parçacıklı kod yazıyor, bu da konuları ortadan kaldırmamız gerektiği anlamına mı geliyor?
Dainius

@Dainius: Aslında evet. IMO varsayılan eşzamanlılık modeli çok işlemli olmalıdır, iki işlemin (1) iletileri birbirine kolayca iletebilmesi ve / veya (2) belleği gerektiği gibi paylaşması için bir yol olmalıdır. Her şeyi paylaşmak istiyorsanız, iş parçacığı oluşturmak yararlıdır , ancak bunu gerçekten istemezsiniz. Tüm adres alanını paylaşarak taklit edilebilir, ancak bu aynı zamanda bir anti-desen olarak kabul edilir.
cHao

@Dainius: Ve elbette, bu dürüstlükten iyiliğe eşzamanlılık gerekliliğini varsayıyor. Genellikle istediğiniz tek şey, işletim sisteminin başka bir şey yapmasını beklerken bir şey yapabilmektir. (Örneğin, bir soketten okurken kullanıcı arayüzünü güncelleme.) İyi bir zaman uyumsuz API, büyük bir çoğunlukta tam açık diş açmayı gereksiz hale getirebilir.
cHao

yani büyük veri matrisiniz varsa, işleme ihtiyacınız varsa, bunu tek bir iş parçacığında yapmak daha iyidir, çünkü birçok insan için bu yanlış yapabilir ..
Dainius

@Dainius: Çoğu durumda, evet. ( Karışıma senkronizasyon eklerseniz, dişler sizi gerçekten yavaşlatabilir . Bu, çoklu iş parçacığının neden çoğu insanın ilk düşüncesi olmaması gerektiğinin en iyi örneğidir.) Diğer durumlarda, yalnızca paylaşan iki işlemin olması daha iyi olur gerekli şeyler. Tabii ki, süreçleri hafızayı paylaşacak şekilde ayarlamanız gerekir. Ama açıkçası, bunu bir iyi bir şey . Hangi parçaların paylaşıldığını ve dolayısıyla hangi parçaların diş açısından güvenli olması gerektiğini açıkça söylemenizi (ve ideal olarak, bilmenizi) gerektirir.
cHao

6

İşte henüz kimsenin söylemediği singletonlarla ilgili bir şey daha var.

Çoğu durumda "singletonity", arabiriminin karakteristiği yerine bazı sınıflar için uygulamanın bir detayıdır. Kontrol Kapsayıcısının ters çevrilmesi bu özelliği sınıf kullanıcılarından gizleyebilir; sadece sınıfınızı bir singleton olarak işaretlemeniz gerekir ( @Singletonörneğin Java'da ek açıklama ile ) ve hepsi bu; IoCC gerisini halleder. Erişim zaten IoCC tarafından yönetildiğinden, singleton örneğinize küresel erişim sağlamanız gerekmez. Bu nedenle IoC Singletons ile ilgili yanlış bir şey yok.

IoC Singleton'ların tersine GoF Singleton'ların, getInstance () yöntemiyle arabirimdeki "singletonity" yi göstermeleri ve böylece yukarıda belirtilen her şeyden muzdarip olmaları gerekir.


3
Kanımca, "singletonity" bir çalışma zamanı ortam ayrıntısıdır ve sınıf kodunu yazan programcı tarafından dikkate alınmamalıdır. Daha ziyade, USER sınıfı tarafından dikkate alınması gereken bir husustur. kaç kullanıcı gerçekten gerekli olduğunu bilir.
Dünya Motoru

5

Singletons , düzgün ve minimal bir şekilde kullanırsanız kötü değildir . Bir noktada singleton'un ihtiyaçlarını değiştiren başka birçok iyi tasarım deseni vardır (ve ayrıca en iyi sonuçları verir). Ancak bazı programcılar bu iyi kalıpların farkında değildir ve singleton'u onlar için kötülük yapan tüm durumlar için kullanır.


Müthiş! Kesinlikle katılmak! Ama çok daha fazla ayrıntı verebilirdiniz ve belki de yapmalısınız. Hangi tasarım modellerinin en yaygın olarak göz ardı edildiğini ve "düzgün ve minimal" tekilleri nasıl kullanacağını genişletmek gibi. Daha kolay daha söyledi bitti ! : P
cregox

Temel olarak alternatif, nesnelere küresel durum üzerinden erişmek yerine yöntem parametreleri aracılığıyla iletmektir.
LegendLength

5

İlk olarak bir sınıf ve işbirlikçileri öncelikle bağımlılara odaklanmak yerine amaçlanan amaçlarını yerine getirmelidir. Yaşam döngüsü yönetimi (örnekler oluşturulduğunda ve kapsam dışına çıktıklarında) sınıf sorumluluğunun bir parçası olmamalıdır. Bunun için kabul edilen en iyi uygulama, bağımlılık enjeksiyonunu kullanarak bağımlılıkları yönetmek için yeni bir bileşen oluşturmak veya yapılandırmaktır.

Genellikle yazılım daha karmaşık hale gelir ve Singleton sınıfının birden fazla bağımsız örneğinin farklı durumlara sahip olması mantıklıdır. Bu tür durumlarda singleton'u ele geçirmek için kod vermek yanlıştır. Kullanmak Singleton.getInstance()küçük basit sistemler için uygun olabilir, ancak aynı sınıfın farklı bir örneğine ihtiyaç duyulduğunda çalışmaz / ölçeklenmez.

Hiçbir sınıf tekil olarak düşünülmemeli, bunun yerine kullanımının veya bağımlıları yapılandırmak için nasıl kullanıldığının bir uygulaması olmalıdır. Hızlı ve kötü bir şey için önemli değil - sadece luke sabit kodlama dosya yollarının önemli olmadığını söylüyor, ancak daha büyük uygulamalar için bu bağımlılıkların DI kullanılarak daha uygun bir şekilde hesaplanması ve yönetilmesi gerekiyor.

Singleton'un testte neden olduğu sorunlar, sabit kodlu tek kullanımlık durum / ortamın bir belirtisidir. Test takımı ve birçok testin her biri bireyseldir ve tekli kodlama ile uyumlu olmayan bir şeyi ayırır.


4

Temelde nesneye yönelik global değişkenler olduklarından, sınıflarınızı genellikle onlara ihtiyaç duymayacak şekilde tasarlayabilirsiniz.


Sınıfınız bir kez örnekle sınırlı değilse, sınıfınızda semaforlar tarafından yönetilen ve hemen hemen aynı şeyle ortaya çıkan statik üyelere ihtiyacınız olacak! Önerilen alternatifin nedir?
Şubat'ta 10:10

Ben bir "MainScreen" ile büyük bir uygulama var bu ekran birçok küçük modal / modal olmayan pencereler / UI formları açar. IMHO MainScreen, örneğin uygulamanın uzak köşesinde bir yerde bir widget MainScreen durum çubuğunda durumunu göstermek istiyor, tek yapmanız gereken MainScreen.getInstance (). "Bazı metin"); Alternatif olarak ne öneriyorsunuz? MainScreen tüm uygulama üzerinden geçmek ?? : D
Salvin Francis

2
@SalvinFrancis: Ne tavsiye ederim, onlar umurumda değil şeyler şeyler umurumda ve birbirleri ile uğraşmak için app gizlice nesneler sahip durdurmak olduğunu. :) Örneğiniz olaylar için çok daha iyi olur. Olayları doğru bir şekilde yaptığınızda, bir widget'ın bir MainScreen'in olup olmadığını hiç önemsemesi gerekmez; sadece "hey, bir şeyler oldu" yayınlıyor ve "bir şeyler oldu" etkinliğine abone olan her şey (bir MainScreen, WidgetTest veya tamamen başka bir şey olsun!) nasıl yanıt vermek istediğine karar verir. OOP'un zaten böyle yapılması gerekiyordu . :)
cHao

1
@Salvin Birçok bileşen tarafından 'sessizce' güncelleniyorsa, hata ayıklama sırasında MainScreen hakkında mantık yürütmenin ne kadar zor olduğunu düşünün. Örneğiniz, singletonların kötü olmasının mükemmel bir nedenidir.
LegendLength
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.