Singleton: Nasıl kullanılmalı


291

Düzenleme: Başka bir sorudan singletons hakkında birçok soru / cevap bağlantıları olan bir cevap sağladım: Singletons hakkında daha fazla bilgi burada:

Bu yüzden konu okudum Singletons: iyi tasarım veya koltuk değneği?
Ve tartışma hâlâ devam ediyor.

Tekilleri Tasarım Deseni olarak görüyorum (iyi ve kötü).

Singleton ile ilgili sorun Kalıp değil, kullanıcılar (özür dileriz). Herkes ve babaları bir tanesini doğru bir şekilde uygulayabileceklerini düşünüyor (ve yaptığım birçok röportajdan çoğu insan yapamıyor). Ayrıca herkes doğru bir Singleton uygulayabileceğini düşündüğü için Deseni kötüye kullanır ve uygun olmayan durumlarda kullanır (global değişkenleri Singletons ile değiştirir!).

Bu yüzden cevaplanması gereken ana sorular:

  • Ne zaman Singleton kullanmalısın
  • Bir Singleton'u doğru şekilde nasıl uygularsınız

Bu yazı için umudum, tek bir yerde (google ve birden fazla sitede arama yapmak yerine) tek bir yerde toplayabilmemizdir. Ayrıca, neden işe yaramadıklarını ve iyi uygulamalar için zayıflıklarını açıklayan Anti-Usages ve yaygın kötü uygulamaların bir listesi de uygun olacaktır.


Bu yüzden topu yuvarlayın:
Elimi tutup kullanacağımı söyleyeceğim ama muhtemelen problemleri var.
"Etkili C ++" kitaplarında konunun "Scott Myers" kullanımını seviyorum

Singleton kullanmak için iyi durumlar (çok değil):

  • Günlük çerçeveleri
  • İplik geri dönüşüm havuzları
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

TAMAM. Biraz eleştiri ve diğer uygulamaları bir araya getirelim.
:-)


36
Daha sonra birden çok kaydedici istediğinize karar verirseniz ne olur? Veya birden fazla iş parçacığı havuzu? Yalnızca bir günlükçü istiyorsanız, yalnızca bir örnek oluşturun ve genel yapın. Singletons sadece kesinlikle orada sadece biri olmak gerekiyor ve küresel olması gerekiyorsa, IMHO iyidir.

3
Kim bir çerçeve sadece 1 logger örneği olabilir dedi. Framework'ü temsil eden tek bir şarkı. Ardından çerçeveleme size belirli kaydediciler verebilir.
Martin York

Evet. Ben bir threadpool olarak bir singeltong kullanmak olmaz. Sadece cevapları ateşlemek için fikirler fırlatmak.
Martin York

@Dan Singleton, strateji modelini uygular. Davranış singletondan soyutlanır. Singleton, tek bir giriş noktasıdır. İki kaydediciniz yok, nasıl kaydedeceğinize karar verebilecek bir kaydedici var. Bir kerede yalnızca bir günlüğe çıktı gönderemezsiniz, ikisine sahip olmanıza gerek yoktur.
Lee Louviere

5
Xaade: iki dosyaya giriş yapmak istiyorsanız ne olacak? Veya bir veritabanına mı? Yoksa bir ağ soketi mi? Veya bir GUI widget'ı mı? Önemli olan, yapay kısıtlamalar eklemeyin - buna gerek yoktur. Ne kadar sıklıkla tek bir döngü yerine döngüler için iki tane oluşturdunuz? Yalnızca bir günlükçü istiyorsanız, yalnızca bir günlükçü oluşturun.

Yanıtlar:


182

Hepiniz yanılıyorsunuz. Soruyu okuyun. Cevap:

Aşağıdaki durumlarda bir Singleton kullanın:

  • Sistemde bir türden yalnızca bir nesne olması gerekir

Aşağıdaki durumlarda Singleton kullanmayın:

  • Hafızadan tasarruf etmek istiyorsunuz
  • Yeni bir şey denemek istiyorsun
  • Ne kadar bildiğini göstermek istiyorsun
  • Çünkü herkes bunu yapıyor (Bkz . Wikipedia'da kargo kült programcısı )
  • Kullanıcı arayüzü widget'larında
  • Önbellek olması gerekiyordu
  • Dizelerde
  • Oturumlarda
  • Gün boyu gidebilirim

En iyi singleton nasıl oluşturulur:

  • Daha küçük, daha iyi. Ben minimalistim
  • İpliğin güvenli olduğundan emin olun
  • Asla boş olmadığından emin ol
  • Yalnızca bir kez oluşturulduğundan emin olun
  • Tembel veya sistem başlatma? İhtiyaçlarınıza kadar
  • Bazen işletim sistemi veya JVM sizin için singleton oluşturur (örneğin Java'da her sınıf tanımı bir singletondur)
  • Bir yıkıcı sağlayın veya bir şekilde kaynakları nasıl atacağınızı öğrenin
  • Çok az bellek kullanın

14
Aslında bence sen de doğru değilsin. Ben başka bir şekilde ifade ediyorum: "Eğer varsa gerek bir ve sistemde bir türden yalnızca bir nesne olması VE gerek kendisine küresel erişim için" ihtiyacı vurgu bana ait - elverişli ise, bunu yapmayın sadece eğer ZORUNLU.

91
Sen de yanılıyorsun. Bir ve tek bir nesneye ihtiyacınız varsa, bir ve yalnızca bir nesne oluşturursunuz. İki vakanın uygulamayı geri döndürülemez şekilde bozmadan konaklayabileceği mantıklı bir yolu yoksa, bunu bir singleton yapmayı düşünmelisiniz. Ve başka bir yönü daha var, küresel erişim: Örneğe küresel erişime ihtiyacınız yoksa, bu tekil olmamalıdır.
jalf

4
Modifikasyon için kapalı, uzatma için açık. Sorun şudur ki, bir singletonu bir duoton veya tripleton olarak genişletemezsiniz. Tek başına sıkışmış.
Lee Louviere

2
@ enzom83: Bir başkent-S Singleton, tekliğini sağlamak için kod içerir. Eğer yalnızca bir örneğini isterseniz, bu kodu kaybedebilir ve sadece size tek örneğinin bellek tasarrufu sağlayan ... bir örneğini kendiniz oluşturabilir, artı tekliği-zorlama kod önlenmesinin gelen tasarruf - Ayrıca ödün değil demektir gereksinimleriniz değişirse ikinci bir örnek oluşturma yeteneği.
cHao

4
Msgstr "Eğer sistemde bir ve sadece bir nesneye ihtiyacınız varsa" - "... ve asla bir birim testinde bu nesneyi alay etmek istemiyorsanız."
Cygon

72

Teklitonlar, iki kötü özelliği bir sınıfta birleştirebilmenizi sağlar. Bu hemen hemen her şekilde yanlış.

Bir singleton size şunları sağlar:

  1. Bir nesneye küresel erişim ve
  2. Bu türden birden fazla nesnenin oluşturulamayacağının garantisi

Bir numara anlaşılır. Globaller genellikle kötüdür. Gerçekten ihtiyacımız olmadıkça, nesneleri asla küresel olarak erişilebilir yapmamalıyız .

İkincisi mantıklı gelebilir, ama düşünelim. En son ne zaman mevcut bir nesneye başvurmak yerine yanlışlıkla yeni bir nesne oluşturdunuz? Bu C ++ olarak etiketlendiğinden, bu dilden bir örnek kullanalım. Sıklıkla yanlışlıkla yazar mısın

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

Ne zaman yazmak istedin?

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

Tabii ki değil. Bu hataya karşı korumaya ihtiyacımız yok, çünkü bu tür bir hata oluşmuyor. Eğer öyleyse, doğru cevap eve gitmek ve 12-20 saat uyumak ve daha iyi hissetmenizi ummaktır.

Yalnızca bir nesneye ihtiyaç duyulursa, yalnızca bir örnek oluşturun. Bir nesneye global olarak erişilebilir olması gerekiyorsa, onu global yapın. Ancak bu, başka örnekleri oluşturmanın imkansız olması gerektiği anlamına gelmez.

"Tek bir örnek mümkündür" kısıtı bizi olası hatalara karşı gerçekten korumaz. Ama yok gözden geçirmeniz çok sert kodumuzu yapmak ve sürdürmek. Oldukça sık öğrenmek Çünkü sonradan daha bir örneği değil gerek yoktu söyledi. Biz bunu biz birden fazla veritabanı var , biz birkaç kaydediciler birden fazla yapılandırma nesnesi istiyorsun var. Birim testlerimiz, ortak bir örnek olarak her testte bu nesneleri oluşturabilir ve yeniden oluşturabilir.

Bir tekil ancak ve ancak kullanılması gerektiğini Yani ihtiyacımız hem sunduğu özellikleri: Biz ise gerek küresel erişimi (globaller genellikle tavsiye edilmez, çünkü nadir olduğu) ve biz ihtiyacımız kimse önlemek için şimdiye a birden fazla örneğini oluşturarak (bana bir tasarım sorunu gibi geliyor). Bunun için görebilmemin tek nedeni, iki örnek oluşturmanın uygulama durumumuzu bozmasıdır - muhtemelen sınıf bir dizi statik üye veya benzer bir saçmalık içerdiğinden. Bu durumda bariz cevap bu sınıfı düzeltmektir. Tek örnek olmaya bağlı olmamalı.

Bir nesneye global erişime ihtiyacınız varsa, onu global gibi yapın std::cout. Ancak oluşturulabilecek örnek sayısını kısıtlamayın.

Kesinlikle, bir sınıfın örnek sayısını bir taneyle sınırlamanız gerekiyorsa ve ikinci bir örnek oluşturmanın güvenli bir şekilde ele alınmasının bir yolu yoksa, bunu zorlayın. Ancak küresel olarak da erişilebilir yapmayın.

Her iki özelliğe de ihtiyacınız varsa, o zaman 1) bunu tek birton haline getirin ve 2) bana bunun için neye ihtiyacınız olduğunu bildirin, çünkü böyle bir durumu hayal etmekte zorlanıyorum.


3
ya da küresel hale getirebilir ve bir singletonun dezavantajlarından sadece birini alabilirsiniz . Singleton ile eşzamanlı olarak kendinizi bu veritabanı sınıfının bir örneğiyle sınırlarsınız. Neden bunu yapıyorsun? Ya da neden bu kadar çok bağımlılığa sahip olduğunuzu anlayabilirsiniz, örnekleme listesi "gerçekten uzun" olur. Hepsi gerekli mi? Bazıları diğer bileşenlere devredilmeli mi? Belki de bazıları bir yapı içinde bir araya getirilebilir, böylece onları tek bir argüman olarak aktarabiliriz. Hepsi teklitonlardan daha iyi olan birçok çözüm var.
jalf

6
Evet, orada bir singleton haklı olabilir . Ama bence sadece oldukça egzotik durumlarda bunun gerekli olduğunu kanıtladınız. Çoğu yazılım kar sürme donanımıyla uğraşmaz. Ama hala ikna olmadım. Gerçek başvurunuzda bunlardan sadece birini istediğinizi kabul ediyorum. Peki ya birim testleriniz? Her biri tek başına çalışmalı, bu yüzden ideal olarak kendi SpreaderController'ını yaratmalılar - ki bu tek birtonla yapmak zor. Son olarak, iş arkadaşlarınız neden ilk etapta birden fazla örnek oluşturacaklar? Bu, korunmak için gerçekçi bir senaryo mu?
jalf

3
Ve kaçırdığınız bir nokta, son iki örneğinizin "tek örnek" sınırlamasını tartışmalı olarak haklı göstermesine rağmen, "küresel olarak erişilebilir" olanı haklı çıkarmak için hiçbir şey yapmamalarıdır. Neden tüm kod tabanı telefon anahtarının yönetim birimine erişebilmeli? Tek birtonun amacı size her iki özelliği de vermektir . Sadece birine ihtiyacınız varsa, bir singleton kullanmamalısınız.
jalf

2
@ jalf - Amacım size Singleton'ın vahşi doğada nerede yararlı olduğuna dair bir örnek vermekti, çünkü hayal bile edemezdiniz; Sanırım şu anki iş kolunuza uygulamak için pek çok kez görmüyorsunuz. Yalnızca Singleton kullanmama izin vereceği için iş uygulamalarından kar küreme programlamasına geçtim. :) j / k Bu şeyleri yapmanın daha iyi yolları olduğu fikrine katılıyorum, düşünmem için bana çok şey verdin. Tartışma için teşekkürler!
J. Polfer

2
İnsanların daha fazla örnek oluşturmasını önlemek için singleton ( AHEM! ) "Desenini" kullanmak, insanların yanlışlıkla bunu yapmasını önlemek için basit bir aptallıktır. Küçük işlevimde Foo türünde yerel bir değişken foo1 varsa ve işlevde yalnızca bir tane istediğimde, birisinin ikinci bir Foo değişken foo2 oluşturacağından ve bunu orijinal yerine kullanacağından endişelenmiyorum.
Thomas Eding

36

Tektonlarla ilgili sorun onların uygulanması değildir. İkisi de açıkça istenen iki farklı kavramı birleştiriyorlar.

1) Tektonlar bir nesneye genel erişim mekanizması sağlar. İyi tanımlanmış bir başlatma sırası olmayan dillerde marjinal olarak daha güvenli veya marjinal olarak daha güvenilir olmalarına rağmen, bu kullanım hala küresel bir değişkenin ahlaki eşdeğeridir. Bu, bazı garip sözdiziminde (g_foo yerine demek) foo :: get_instance () giyinmiş küresel bir değişkendir, ancak aynı amaca hizmet eder (tüm programda erişilebilir tek bir nesne) ve aynı dezavantajlara sahiptir.

2) Tektonlar bir sınıfın birden çok örneğini önler. IME, bu tür özelliklerin bir sınıfa dönüştürülmesi nadirdir. Normalde çok daha bağlamsal bir şeydir; bire bir olarak kabul edilen birçok şey gerçekten sadece bire bir olur. IMO daha uygun bir çözüm, yalnızca birden fazla örneğe ihtiyacınız olduğunu fark edene kadar yalnızca bir örnek oluşturmaktır.


6
Kabul. İki yanlış gerçek dünyadaki bazılarına göre doğru yapabilir. Ancak programlamada, iki kötü fikri karıştırmak iyi bir fikirle sonuçlanmaz.
jalf

27

Desenli bir şey: genelleme . Yararlı oldukları ve başarısız oldukları zaman tüm vakaları vardır.

Singleton , kodu test etmeniz gerektiğinde kötü olabilir . Genellikle sınıfın bir örneğiyle sıkışıp kalıyorsunuz ve yapıcıda bir kapı açmak veya durumu sıfırlamak için bazı yöntemler arasında seçim yapabilirsiniz.

Diğer sorun, Singleton'un aslında kılık değiştirmiş küresel bir değişkenten başka bir şey olmamasıdır . Programınız üzerinde çok fazla küresel ortak durumunuz olduğunda, işler geri dönme eğilimindedir, hepimiz biliyoruz.

Bu yapabilir izleme bağımlılık zor. Her şey Singleton'unuza bağlı olduğunda, onu değiştirmek, ikiye bölmek vb. Daha zordur. Genellikle onunla sıkışıp kalırsınız. Bu aynı zamanda esnekliği de engeller. Bu sorunu hafifletmek için bazı Bağımlılık Enjeksiyonu çerçevesini araştırın .


8
Hayır, bir single, kılık değiştirmiş küresel bir değişkenden çok daha fazlasıdır. Özellikle kötü yapan da bu. O başka bir konsept ile (genellikle kötü) küresel-lik birleştirir da (o bir örneğini ihtiyacı karar verirse bir sınıf örneğini programcı izin vermeyecek gibi o) Genellikle olan kötü kullanılmış evet, küresel değişkenler olarak. Ve sonra diğer kötü yan etkiyi de sürüklerler ve kod tabanını saklarlar.
jalf

7
Ayrıca, tek tektonların kamuya açık olması gerekmediğine dikkat edilmelidir. Bir singleton çok iyi kütüphanenin içinde olabilir ve asla kullanıcıya maruz kalmaz. Yani bu anlamda mutlaka "küresel" değiller.
Steven Evers

1
Tekil testlerde ne kadar acı olduğunu göstermek için +1.
DevSolar

1
@jalf Bir kişinin birden fazla sınıf örneği oluşturmasına izin vermemek kötü bir şey değildir. Gerçeği zorunlu kılan sınıfın sadece bir örneği olacaksa. Birisi daha sonra başka bir örnek oluşturması gerektiğine karar verirse, onu yeniden düzenlemelidir, çünkü ilk etapta asla tekil olmamalıdır.
William

2
@William: ve zaman zaman birden fazla kaydedicim olması gerekiyordu. Bir singleton için değil, eski düz bir yerel için tartışıyorsunuz. Bir kaydedicinin her zaman kullanılabilir olduğunu bilmek istersiniz . Küresel bir amaç budur. Başka hiçbir kaydedicinin somutlaştırılamayacağını bilmenize gerek yoktur , bu da tek birtonun uyguladığı şeydir. (kaydediciniz için birim testleri yazmayı deneyin - gerektiğinde oluşturabilir ve yok ederseniz bu çok daha kolaydır ve bu tek
birtonla

13

Singletons temel olarak, karmaşık küresel değişkenlere sahip olmanızı zorlaştıran veya olanaksız hale getiren dillerde karmaşık küresel duruma sahip olmanızı sağlar.

Özellikle Java, global değişkenlerin yerine singleton kullanır, çünkü her şey bir sınıf içinde yer almalıdır. Global değişkenlere en yakın, genel statik değişkenlerdir ve bunlar küreselmiş gibi kullanılabilir.import static

C ++ genel değişkenlere sahiptir, ancak genel sınıf değişkenlerinin kurucularının çağrılma sırası tanımsızdır. Bu nedenle, bir singleton, bu değişkene ilk ihtiyaç duyulana kadar bir global değişkenin oluşturulmasını ertelemenizi sağlar.

Python ve Ruby gibi diller tektonları çok az kullanır, çünkü bunun yerine bir modül içindeki global değişkenleri kullanabilirsiniz.

Peki ne zaman bir singleton kullanmak iyi / kötü? Hemen hemen küresel bir değişken kullanmanın iyi / kötü olacağı zaman.


Global bir değişken ne zaman "iyi"? Bazen bir sorun için en iyi geçici çözümdür, ancak asla "iyi" değildirler.
DevSolar

1
global değişken her yerde kullanıldığında iyidir ve her şeye erişebilir. Tek durumlu bir turing makinesinin uygulanması tek bir tondan yararlanabilir.
Lee Louviere

Ben bu cevapta dolaylı katmanı seviyorum: "ne zaman küresel kullanmak iyi / kötü olurdu". Hem DevSolar hem de Lee Louviere, cevap verdiğinde kimin yorum yapacağı bilinmese de kabul ettikleri değeri alırlar.
Praxeolitic

6

Alexandrescu tarafından tasarlanan modern C ++ Design , iş parçacığı açısından güvenli, kalıtsal bir jenerik singletona sahiptir.

2p değere göre, teklitonlarınız için yaşam sürelerini tanımlamanızın önemli olduğunu düşünüyorum (bunları kullanmak için kesinlikle gerekli olduğunda). Normalde statik get()fonksiyonun hiçbir şey başlatmasına izin vermem ve kurulum ve yıkımı ana uygulamanın bazı özel bölümlerine bırakmam. Bu, tektonlar arasındaki bağımlılıkları vurgulamaya yardımcı olur - ancak yukarıda vurgulandığı gibi, mümkünse bunlardan kaçınmak en iyisidir.


6
  • Bir Singleton'u doğru şekilde nasıl uygularsınız

Daha önce hiç görmediğim bir konu var, bir önceki işte karşılaştığım bir şey. DLL'ler arasında paylaşılan C ++ singletonları vardı ve bir sınıfın tek bir örneğinin çalışmadığından emin olmanın olağan mekanikleri işe yaramadı. Sorun, her DLL EXE ile birlikte kendi statik değişkenler kümesi olmasıdır. Get_instance işleviniz satır içi veya statik kitaplığın parçasıysa, her DLL kendi "singleton" kopyasıyla dolar.

Çözüm, singleton kodunun yalnızca bir DLL veya EXE'de tanımlandığından emin olmak veya örnekleri çözümlemek için bu özelliklere sahip tek bir yönetici oluşturmaktır.


10
Ahbap, Singletons'u sevdiğini duydum, bu yüzden Singleton için bir Singleton yaptım, böylece anti-pattern yaparken anti-desen oluşturabilirsin.
Eva

@Eva, evet böyle bir şey. Sorunu ben yaratmadım, sadece bir şekilde çalışmasını sağlamak zorunda kaldım.
Mark Ransom

5

İlk örnek iş parçacığı için güvenli değildir - iki iş parçacığı aynı anda getInstance öğesini çağırırsa, bu statik bir PITA olacaktır. Bir çeşit muteks yardımcı olabilir.


Yukarıdaki yorumlarda belirtilen evet : * Sınırlama: Tek Dişli Tasarım * Bkz: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Çok iş parçacıklı uygulamalarda kilitleme ile ilgili sorunlar için
Martin York

Statik yöntem olarak yalnızca getInstance özelliğine sahip klasik singleton ve diğer işlemler için örnek yöntemleri hiçbir zaman iş parçacığı için güvenli hale getirilemez. (yerel iş parçacığı kullanarak iş parçacığı başına bir ton yapmazsanız ...)
Tobi

c ++ 11 veya üzeri sürümlerde bile?
hg_git

5

Diğerlerinin de belirttiği gibi, tektonların büyük dezavantajları, bunları genişletememe ve birden fazla örneği örnekleme gücünü kaybetme, örneğin test amaçlıdır.

Tektonların bazı yararlı yönleri:

  1. tembel veya açık örnekleme
  2. kurulum ve / veya durum gerektiren bir nesne için kullanışlı

Ancak, bu avantajlardan yararlanmak için bir singleton kullanmanız gerekmez. İşi yapan normal bir nesne yazabilir ve sonra insanların bir fabrika (ayrı bir nesne) üzerinden erişmesini sağlayabilirsiniz. Fabrika, gerektiğinde yalnızca birini örnekleme ve yeniden kullanma vb. Ayrıca, somut bir sınıftan ziyade bir arayüze program yaparsanız, fabrika stratejileri kullanabilir, yani arayüzün çeşitli uygulamalarını açıp kapatabilirsiniz.

Son olarak, bir fabrika kendini Spring vs. gibi bağımlılık enjeksiyon teknolojilerine borçludur.


3

Singletons, başlattığınızda ve itiraz ederken çok sayıda kod çalıştırıldığında kullanışlıdır. Örneğin, bir kalıcılık nesnesi ayarlarken iBatis'i kullanırken, kodunuza gelmeden önce tüm yapılandırmaları okumalı, haritaları ayrıştırmalı, hepsinin doğru olduğundan emin olmalısınız.

Bunu her seferinde yaparsanız performans çok düşecektir. Tek birtonda kullanarak, bu isabeti bir kez alırsınız ve sonraki tüm aramaların bunu yapması gerekmez.


Prototip Desen de bunu yapar ve daha esnek. Bunu, müşteriniz pahalı sınıfınızın birçok örneğini yapacaksa da kullanabilirsiniz, ancak bunlardan yalnızca sınırlı sayıda farklı durumları vardır. Örneğin, Tetris'deki tetronimos.
Eva

3

Singletons'un asıl çöküşü kalıtımdan kopmalarıdır. Singleton'a başvuruda bulunulan koda erişiminiz yoksa, size genişletilmiş işlevsellik sağlamak için yeni bir sınıf türetemezsiniz. Bu nedenle, Singleton kodunuzu sıkıca bağlayacak olmasının ötesinde (bir Strateji Deseni ... yani Bağımlılık Enjeksiyonu ile sabitlenebilir), kodun bölümlerini revizyondan (paylaşılan kütüphaneler) kapatmanızı da önleyecektir.

Bu nedenle, günlük veya iş parçacığı havuzlarının örnekleri bile geçersizdir ve bunların yerine Stratejiler kullanılmalıdır.


Kaydedicilerin kendileri tekil olmamalıdır. Genel "yayın" mesajlaşma sistemi olmalıdır. Kaydedicilerin kendileri yayın mesajlarına abone olurlar.
CashCow

İş parçacığı havuzları da tek başlı olmamalıdır. Genel soru şu ki, bunlardan birden fazlasını ister misiniz? Evet. En son kullandığımda bir uygulamada 3 farklı iş parçacığı havuzumuz vardı.
CashCow

3

Çoğu insan küresel bir değişken kullanma konusunda kendilerini iyi hissetmeye çalışırken singletonları kullanır. Meşru kullanımlar vardır, ancak çoğu zaman insanlar bunları kullandıklarında, sadece bir örnek olabileceği gerçeği, küresel olarak erişilebilir olduğu gerçeğine kıyasla sadece önemsiz bir gerçektir.


3

Tek birton yalnızca bir örneğin oluşturulmasına izin verdiğinden, örnek çoğaltmasını etkin bir şekilde kontrol eder. örneğin, bir aramanın birden fazla örneğine ihtiyacınız olmaz - örneğin, bir mors arama haritası, böylece tek bir sınıfta sarmak uygundur. Ve sınıfın tek bir örneğine sahip olmanız, o örneğe yapılan başvuruların sayısıyla sınırlı olduğunuz anlamına gelmez. Örneğe çağrıları (iş parçacığı sorunlarını önlemek için) sıralayabilir ve gerekli efekt değişikliklerini yapabilirsiniz. Evet, bir singletonun genel biçimi küresel olarak herkese açık bir formdur, daha sınırlı bir singleton oluşturmak için tasarımı kesinlikle değiştirebilirsiniz. Bunu daha önce hiç yormadım ama eminim ki mümkün. Ve singleton deseninin tamamen kötü olduğunu söyleyen herkese şunu bilmelisiniz:



2

Aşağıda, hafızayı yıkıcıda yeniden konumlandıran, iş parçacığı için güvenli bir tekli model uygulamak için daha iyi bir yaklaşım bulunmaktadır. Ancak yıkıcı isteğe bağlı olmalıdır, çünkü program sona erdiğinde singleton örneği otomatik olarak yok edilecektir:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

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

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

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

Tekli sınıfları kullanmamız gereken durumlarla ilgili olarak şunlar olabilir: Programın yürütülmesi sırasında örneğin durumunu korumak istiyorsak, dosyanın yalnızca bir örneğinin gerekli olduğu bir uygulamanın yürütme günlüğüne yazmakla ilgileniyorsak kullanılabilir .... vb. Yukarıdaki kodumda optimizasyon önerisi olup olmadığı takdir edilecektir.


2
Bu kesinlikle daha iyi değil. 1: Sahiplik semantiği işaretçiyi kullanarak tanımlamıyorsunuz. Onları yönetmeye hazır olmadıkça asla C ++ 'da işaretçiler kullanmamalısınız. 2: Çift kontrollü kilitleme kullanımınız eskidir ve bunu yapmanın çok daha modern yolları vardır. 3: İmha hakkındaki yorumlarınız saf. Belleğin geri kazanımı temizlikle ilgili yıkıcı kullanım noktası değildir. Daha iyi bir sürüm için öneriler: Soruya bakın. Orada sunulan sürüm zaten çok daha iyi.
Martin York

1

Singletons'ı röportaj testi olarak kullanıyorum.

Bir geliştiriciden tasarım desenlerini adlandırmasını istediğimde, isimlendirebilecekleri tek Singleton ise işe alınmaz.


45
İşe alma konusundaki sert ve hızlı kurallar, çok çeşitli potansiyel çalışanları kaçırmanızı sağlayacaktır.
Karl

13
Çok çeşitli aptallar var. Bu, işe alınmaları gerektiği anlamına gelmez. Birisi tasarım deseninden hiç bahsetmezse, singleton'u bilen birine ve başka desenlere tercih edilmeyeceğini düşünüyorum.
jalf

3
Kayıt defteri için - cevabım yanaktaki dildi. Gerçek röportaj sürecimde, C ++ 'da birini eğitmemiz gerekip gerekmediğini ve bunun ne kadar zor olacağını değerlendirmeye çalışıyorum. En sevdiğim adaylardan bazıları C ++ 'ı içte ve dışta bilmeyen insanlar, ancak onlarla bu konuda harika bir konuşma yapabildim.
Matt Cruikshank

4
Aşağı oy. Kişisel deneyimimden - programcı Singleton dışında başka bir desen söyleyemeyebilir, ancak bu Singletons kullandığı anlamına gelmez. Şahsen, onları duymadan ÖNCE kodumda singletons kullanıyordum (onlara "daha akıllı küreseller" dedim - küreselin ne olduğunu biliyordum). Onları öğrendiğimde, artılarını ve eksilerini öğrendiğimde onları kullanmayı bıraktım. Aniden durduğumda Birim testi benim için çok daha ilginç hale geldi ... Bu beni daha kötü bir programcı yaptı mı? Pfff ...
Paulius

3
Ben de "bazı tasarım kalıpları adlandırma" saçma soru için aşağı indiriyorum. Tasarım, sadece isimlerini ortaya çıkarmakla kalmayıp tasarım desenlerinin nasıl uygulanacağını anlamakla ilgilidir. Tamam, bu bir aşağı oyu garanti etmeyebilir, ancak bu cevap troll-ish.
CashCow

0

Anti-Kullanımı:

Aşırı tek ton kullanımıyla ilgili önemli bir sorun, modelin alternatif uygulamaların kolayca genişletilmesini ve değiştirilmesini önlemesidir. Sınıf adı, singletonun kullanıldığı her yerde sabit kodlanmıştır.


2 nedenden ötürü indirildi: 1. Singleton dahili olarak polimorfik örnekleri kullanabilir (örneğin, global Logger hedefleme polimorfik stratejilerini kullanır) 2. Singleton adı için typedef olabilir, bu yüzden kod aslında typedef'e bağlıdır.
topright gamedev

Merakla tekrar eden şablon desenini kullanarak bir singleton versiyonumun genişletilebilir olmasını sağladım.
Zachary Kraus

0

Bu C # için en sağlam sürüm olduğunu düşünüyorum :

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

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

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

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

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

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

  // "Singleton"

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

    private Random random = new Random();

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

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

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

      return instance;
    }

    // Simple, but effective random load balancer

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

İşte .NET için optimize edilmiş sürüm :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

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

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

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

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

  // Singleton

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

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

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

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

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

Bu kalıbı dotfactory.com adresinde bulabilirsiniz .


3
Kodun okunmasını kolaylaştırmak için Singletons ile özellikle ilgili olmayan parçaları çıkarabilirsiniz.
Martin York

Ayrıca, ilk sürümünüz olası okuma / yazma yeniden sıralama nedeniyle iş parçacığı için güvenli değildir. Bkz. Stackoverflow.com/questions/9666/…
Thomas Danecker

5
Ah ... yanlış dil mi? Soru oldukça açık bir şekilde C ++ ile etiketlenmiştir .
DevSolar

0

Meyers singleton deseni çoğu zaman yeterince iyi çalışır ve bazen daha iyi bir şey aramak için ödeme yapmaz. Yapıcı asla atmayacağı ve singletonlar arasında bağımlılık olmadığı sürece.

Tekilton, tüm GAO'lar tekil olmasa da, küresel olarak erişilebilir bir nesne (bundan sonra GAO) için bir uygulamadır .

Kaydedicilerin kendileri tekil olmamalı, ancak günlük iletisinin nereden veya nasıl kaydedildiği yerden ayrıştırmak için günlük araçlarına ideal olarak küresel erişilebilir olmalıdır.

Tembel yükleme / tembel değerlendirme farklı bir kavramdır ve singleton genellikle bunu da uygular. Özellikle iş parçacığı güvenliği ve istisnalarla başarısız olursa, o zaman iyi bir fikir gibi görünen şeylerin o kadar da büyük olmadığı ortaya çıkar. (Dizelerde COW uygulaması gibi).

Bunu akılda tutarak, GOA'lar şu şekilde başlatılabilir:

namespace {

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

}

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

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

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

Bu kadar kabaca yapılmasına gerek yoktur ve nesneleri içeren yüklü bir kütüphanede, ömürlerini yönetmek için muhtemelen başka bir mekanizma olmasını istersiniz. (Kütüphaneyi yüklediğinizde aldığınız bir nesneye koyun).

Teklitonları ne zaman kullanacağım? Onları 2 şey için kullandım - dlopen ile hangi kitaplıkların yüklendiğini gösteren tek bir tablo - Kaydedicilerin abone olabileceği ve mesaj gönderebileceğiniz bir mesaj işleyici. Özellikle sinyal işleyiciler için gereklidir.


0

Hala neden bir singletonun küresel olması gerektiğini anlamıyorum.

Ben bir özel sabit statik değişken olarak sınıf içinde bir veritabanı sakladı ve hiç kullanıcıya veritabanı maruz kalmadan veritabanını kullanan sınıf işlevleri yapmak bir singleton üretecekti.

Bu işlevin neden kötü olacağını anlamıyorum.


Neden küresel olduğunu düşündüğünüzü anlamıyorum.
Martin York

Bu konuya göre, Herkes tek bir tektonun küresel olması gerektiğini söylüyordu
Zachary Kraus

1
Hayır. İş parçacığı, bir singelton küresel durumuna sahip olduğunu gösterir. Global bir değişken olduğu için değil. Önerdiğiniz çözümün küresel durumu var. Önerdiğiniz çözüm aynı zamanda küresel bir değişken kullanmaktır; bir sınıfın statik bir üyesi "Statik Depolama Süresi" nin bir nesnesidir. global değişken "Statik Depolama Süresi" nin bir nesnesidir. Böylece ikisi temel olarak biraz farklı anlambilim / kapsamlarla aynı şeydir.
Martin York

Özel statik değişken "Statik Depolama Süresi" nedeniyle hala küresel mi?
Zachary Kraus

1
Not: Belirttiğim hiçbir şeyi kasten özledim. Statik bir "özel" üye kullanma tasarımınız, singelton ile aynı şekilde kötü değildir. Çünkü "küresel değişebilir devlet" i tanıtmaz. Ama bu bir singleton da değil. Tekton, nesnenin yalnızca bir örneğinin bulunabilmesi için tasarlanmış bir sınıftır. Önerdiğiniz şey, bir sınıfın tüm nesneleri için tek bir paylaşılan durumdur. Farklı bir kavram.
Martin York

0

Çok fazla belleğe sahip bir sınıfım olduğunda onları kullanışlı buluyorum. Örneğin son zamanlarda üzerinde çalıştığım bir oyunda, bitişik belleğin çok büyük dizilerini içeren bir etki haritası sınıfım var. Ben başlangıçta tüm tahsis istiyorum, tüm kapanışta serbest ve kesinlikle sadece bir kopyasını istiyorum. Aynı zamanda birçok yerden de erişmem gerekiyor. Singleton paterninin bu durumda çok faydalı olduğunu düşünüyorum.

Eminim başka çözümler de var ama bunu çok kullanışlı ve kolay uygulanabilir buluyorum.


0

Eğer singletonu yaratan ve onu kullanan sizseniz, bunu singleton olarak yapmayın (mantıklı değildir çünkü nesnenin tekilliğini singleton yapmadan kontrol edebilirsiniz) ama bir geliştiricinin ve kullanıcılarınıza yalnızca bir nesne sağlamak istiyorsunuz (bu durumda singletonu kim oluşturuyorsunuz, ancak kullanıcı siz değilsiniz).

Singletons nesnelerdir, bu yüzden onları nesne olarak kullanın, birçok kişi doğrudan döndüren yöntemi çağırarak singletonlara erişir, ancak bu zararlıdır, çünkü kodunuzu nesnenin singleton olduğunu bilirsiniz, singletonları nesne olarak kullanmayı tercih ederim, onları geçiyorum yapıcı aracılığıyla ve onları sıradan nesneler olarak kullanıyorum, bu şekilde, kodunuz bu nesnelerin tekil olup olmadığını bilmiyor ve bu bağımlılıkları daha net hale getiriyor ve yeniden düzenleme için biraz yardımcı oluyor ...


-1

Masaüstü uygulamalarında (artık sadece dinozorlar bunları yazıyor!) Nispeten değişmeyen küresel uygulama ayarları elde etmek için şarttır - aksi takdirde her sınıfa ve her diyaloga girmek zorunda kalacak kullanıcı dili, yardım dosyalarına yol, kullanıcı tercihleri ​​vb. .

Düzenle - elbette bunlar salt okunur olmalıdır!


Ama bu soruya yalvarıyor; neden yardım dosyasına kullanıcı dili ve yol örneklerini yöntem olmak zorunda hiç ?
DrPizza

2
Bunun için küresellerimiz var. Onları tekil yapmaya gerek yok
jalf

Global değişkenler - o zaman onları kayıt defterinden / veritabanından nasıl serileştirirsiniz? Gobal sınıfı - o zaman bunlardan sadece birinin olmasını nasıl sağlıyorsunuz?
Martin Beckett

@mgb: bunları kayıt defterinden / veritabanından değerleri okuyarak ve global değişkenlere kaydederek serileştirirsiniz (bu muhtemelen ana işlevinizin en üstünde yapılmalıdır). sınıfın yalnızca bir nesnesi olduğundan emin olursunuz, sınıfın yalnızca bir nesnesini oluşturarak ... gerçekten ... 'yeni \ + global_class_name' 'grep -rn zor. ? Gerçekten mi?
Paulius

7
@mgb: Neden dünyada sadece bir tane olduğundan emin olalım? Sadece bir örneğin her zaman geçerli ayarları temsil ettiğini bilmem gerekir . ancak yerin etrafında başka ayar nesnelerine sahip olmamın bir nedeni yok. Örneğin, "kullanıcının şu anda tanımladığı, ancak henüz uygulanmadığı ayarlar" için bir tane olabilir. Veya "kullanıcının daha önce kaydettiği yapılandırma, daha sonra onlara geri dönebilmesi için". Veya birim testlerinizin her biri için bir tane.
jalf

-1

Başka bir uygulama

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

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

3
Bu gerçekten korkunç. Daha iyi bir tembel başlatma ve daha da önemlisi garantili deterministik yıkım için stackoverflow.com/questions/1008019/c-singleton-design-pattern/… adresine bakın .
Martin York

Eğer işaretçiler kullanacaksanız, Instance()referans değil, bir işaretçi döndürmelisiniz. .cppDosyanızın içinde örneği null: olarak başlatın Singleton* Singleton::instance_ = nullptr;. Ve Instance()olarak uygulanmalıdır: if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis
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.