Singleton ne zaman uygun olur? [kapalı]


Yanıtlar:


32

Singletons'ın iki ana eleştirisi, gözlemlediğimden iki kampa düşüyor:

  1. Singletons, daha az yetenekli programcılar tarafından kötüye kullanılır ve kötüye kullanılır, bu nedenle her şey bir tekil olur ve Class :: get_instance () referansları ile dolu bir kod görürsünüz. Genel olarak konuşursak, Singleton modelini kullanmaya hak kazanan yalnızca bir veya iki kaynak (örneğin bir veritabanı bağlantısı gibi) vardır.
  2. Singletonlar temel olarak statik sınıflardır ve bir veya daha fazla statik metoda ve özelliğe dayanır. Statik olan her şey, Ünite Testi yapmaya çalıştığınız zaman gerçek ve somut problemler sunar çünkü kodunuzda alay edilemez veya engellenemeyen ölü noktaları temsil ederler. Sonuç olarak, bir Singleton'a (veya başka bir statik yöntem veya sınıfa) dayanan bir sınıfı test ederken, yalnızca o sınıfı değil aynı zamanda statik yöntemi veya sınıfı da test edersiniz.

Bunların ikisinin de sonucu olarak, ortak bir yaklaşım, bu sınıfların tek bir örneğini tutmak için geniş bir konteyner nesnesi oluşturmaktır ve yalnızca konteyner nesnesi, bu sınıfları değiştirebilir, diğerlerinden birçok sınıfa bunlardan erişim izni verilebilir. konteyner nesnesi.


6
Sadece Java ile statik yöntemleri, JMockit ( code.google.com/p/jmockit ) ile çözebileceğinizi söylemek istiyorum . Çok kullanışlı bir araçtır.
Michael K

3
Bazı düşünceler: konteyner bir Singleton, bu yüzden Singleton'dan kurtulmadınız. Bir ortak API ile bir kütüphane yazıyorsanız Ayrıca, kullanmak için API kullanıcıyı zorlayamaz senin konteyner. Kütüphanenizde global bir kaynak yöneticisine ihtiyacınız varsa (örneğin, tek bir fiziksel kaynağa erişimi koruyorsanız), bir Singleton kullanmanız gerekir.
Scott Whitlock

2
@Scott Whitlock neden bir singleton olması gerekiyor? Sırf sadece tek bir örnek olması yeterli değil. Herhangi bir IoC kütüphanesi bu tür bir bağımlılığı
yönetecektir

Bu çözüm aynı zamanda Toolbox
cregox 13:03

41

Bunun bir anti-kalıp olduğuna katılıyorum. Neden? Çünkü, kodunuzun bağımlılıkları hakkında yalan söylemesine izin verir ve diğer programcılara önceden değişmeyen tekillerinizde değişken durum göstermemesine güvenemezsiniz.

Bir sınıf, yalnızca bir dize alan bir kurucuya sahip olabilir, bu nedenle yalıtımda başlatıldığını ve yan etkileri olmadığını düşünürsünüz. Bununla birlikte, sessizce, bir tür halka açık, küresel olarak kullanılabilir tekil nesne ile iletişim kurar, böylece sınıfı başlattığınızda farklı veriler içerir. Bu, yalnızca API'nizin kullanıcıları için değil, aynı zamanda kodun test edilebilirliği için de büyük bir sorundur. Kodu doğru bir şekilde test etmek için, tutarlı test sonuçları elde etmek için mikro-yönetmeniz ve tektondaki küresel durumun farkında olmanız gerekir.


Keşke birden fazla kez oy kullanabilseydim!
MattDavey 13:11

34

Singleton modeli temelde tembel olarak başlatılmış bir global değişkendir. Genel değişkenler genellikle ve haklı olarak kötülük olarak kabul edilir, çünkü programın görünüşte alakasız kısımları arasında ürkütücü harekete izin verirler. Bununla birlikte, IMHO, bir programın başlatma rutininin bir parçası olarak bir yerden (örneğin, bir config dosyasını veya komut satırı argümanlarını okuyarak) ayarlanan ve daha sonra sabit olarak kabul edilen global değişkenlerde yanlış bir şey yoktur. Küresel değişkenlerin bu şekilde kullanılması, derleme zamanında bildirilmiş bir isimsiz sabite sahip olmaktan sadece ruhsal değil harf bakımından farklıdır.

Benzer şekilde, Singletons'a göre, programın görünüşte alakasız kısımları arasında değişebilir bir durumu geçmek için kullanılırsa ve eğer kötüler. Değişken durum içermiyorlarsa veya içerdikleri değişken durum tamamen kapsüllenmişse, nesnenin kullanıcılarının çok iş parçacıklı bir ortamda bile bunu bilmesi gerekmiyorsa, o zaman yanlış bir şey olmaz.


3
Başka bir deyişle, bir veritabanındaki herhangi bir programda kullanılmaya karşısınız.
Kendall Helmstetter Gelner

Fikrinizi vurgulayan ünlü bir google tech talk videosu var youtube.com/watch?v=-FRm3VPhseI
Martin York

2
@KendallHelmstetterGelner: Devlet ve ikincil depolama arasında bir fark var. Bütün bir DB'yi bellekte tutabilirsiniz.
Martin York

7

İnsanlar neden kullanıyor?

PHP dünyasında epeyce singleton gördüm. Haklı gösterileceği şekli bulduğum herhangi bir kullanım durumunu hatırlamıyorum. Ama sanırım insanların neden kullandığı hakkında motivasyon hakkında bir fikrim var.

  1. Tek örnek .

    "Uygulama boyunca tek bir C sınıfı örneği kullanın."

    Bu, örneğin "varsayılan veritabanı bağlantısı" için makul bir gereksinimdir. Asla ikinci bir db bağlantısı kurmayacağınız anlamına gelmez, bu sadece normal olanla çalıştığınız anlamına gelir.

  2. Tek örnekleme .

    “C sınıfının bir defadan fazla başlatılmasına izin vermeyin (işlem başına, istek başına vb.)”

    Bu, sadece sınıfı başlatmanın diğer örneklerle çakışan yan etkileri olması durumunda geçerlidir.

    Genellikle bu çatışmalar, bileşen yeniden tasarlanarak önlenebilir - örneğin, sınıf kurucusunun yan etkilerini ortadan kaldırarak. Veya başka yollarla çözülebilirler. Ancak hala bazı yasal kullanım durumları olabilir.

    Ayrıca, "yalnızca bir" gereksinimin gerçekten "işlem başına bir" anlamına mı geldiğini düşünmelisiniz. Örneğin, kaynak eşzamanlılığı için, bu gereksinim, "işlem başına bir değil" yerine "tüm sistem başına bir işlemdir" anlamına gelir. Ve diğer şeyler için, bunun yerine "uygulama bağlamı" na göre ve sadece işlem başına bir uygulama bağlamına sahip olursunuz.

    Aksi takdirde, bu varsayımı uygulamaya gerek yoktur. Bunu uygulamak, birim testleri için ayrı bir örnek oluşturamayacağınız anlamına da gelir.

  3. Global erişim.

    Bu, yalnızca nesneleri, kullanıldığı yerlere etrafa aktarmak için uygun bir altyapınız yoksa yasaldır. Bu, çerçevenizin veya çevrenizin berbat olduğu anlamına gelebilir, ancak bunu düzeltmek için güçleriniz dahilinde olmayabilir.

    Fiyat sıkı birleşme, gizli bağımlılıklar ve küresel durumla ilgili kötü olan her şeydir. Ama muhtemelen zaten bunlardan acı çekiyorsunuz.

  4. Tembel örnekleme.

    Bu, singleton'un gerekli bir parçası değil, ancak bunları uygulamanın en popüler yolu gibi görünüyor. Ancak tembel bir örnekleme yapmak güzel bir şey olsa da, bunu başarmak için bir singleton'a ihtiyacınız yok.

Tipik uygulama

Tipik uygulama, özel bir kurucu ile bir sınıf ve bir statik örnek değişkeni ve tembel örneklemeli bir statik getInstance () yöntemidir.

Yukarıda belirtilen sorunlara ek olarak, bu, tek sorumluluk ilkesi ile ısırır , çünkü sınıf zaten sahip olduğu diğer sorumluluklara ek olarak, kendi örneklemesini ve yaşam döngüsünü kontrol eder .

Sonuç

Pek çok durumda, aynı sonucu bir singleton olmadan ve global devlet olmadan elde edebilirsiniz. Bunun yerine, bağımlılık enjeksiyonunu kullanmalısınız ve bir bağımlılık enjeksiyon kabını düşünebilirsiniz .

Ancak, aşağıdaki geçerli gerekliliklerin kaldığı kullanım durumları vardır:

  • Tek örnek (ancak tek örnekleme değil)
  • Global erişim (çünkü bronz bir yaş çerçevesi ile çalışıyorsunuz)
  • Tembel örnekleme (çünkü sadece olması güzel)

İşte bu durumda yapabilecekleriniz:

  • Genel bir kurucu ile başlatmak istediğiniz bir C sınıfı oluşturun.

  • Statik bir örnek değişkenli ayrı bir S sınıfı ve örnek için C sınıfını kullanacak olan tembel başlatmaya sahip statik bir S :: getInstance () yöntemi oluşturun.

  • Tüm yan etkilerini C'nin yapıcısından kaldırın. Bunun yerine, bu yan etkileri S :: getInstance () yöntemine yerleştirin.

  • Yukarıdaki gereksinimlere sahip birden fazla sınıfa sahipseniz, sınıf örneklerini küçük bir yerel servis konteyneriyle yönetmeyi ve statik örneği yalnızca konteyner için kullanmayı düşünebilirsiniz . Böylece, S :: getContainer () size tembel bir örnek servis kabı verir ve diğer nesneleri de kaptan alırsınız.

  • Yapabileceğiniz statik getInstance () işlevini çağırmaktan kaçının. Mümkün olduğunda bağımlılık enjeksiyonu kullanın. Özellikle, kap yaklaşımını birbirine bağlı birden fazla nesneyle kullanırsanız, bunlardan hiçbirinin S :: getContainer () öğesini çağırması gerekmez.

  • İsteğe bağlı olarak, C sınıfının uyguladığı bir arabirim oluşturun ve bunu S :: getInstance () öğesinin dönüş değerini belgelemek için kullanın.

(Buna hala singleton mı diyoruz? Bunu yorum bölümüne bırakıyorum ..)

Yararları:

  • Herhangi bir global duruma dokunmadan, ünite testi için ayrı bir C örneği oluşturabilirsiniz.

  • Örnek yönetimi sınıfın kendisinden ayrılır -> endişelerin ayrılması, tek sorumluluk ilkesi.

  • S :: getInstance () uygulamasının örnek için farklı bir sınıf kullanmasına, hatta hangi sınıfı kullanacağınızı dinamik olarak belirlemesine izin vermek oldukça kolay olurdu.


1

Şahsen, 1, 2 veya 3 veya söz konusu sınıf için sınırlı sayıda nesneye ihtiyacım olduğunda singleton kullanacağım. Veya sınıfımın kullanıcısına, sınıfımın düzgün çalışması için birden fazla örneğinin oluşturulmasını istemediğimi iletmek istiyorum.

Ayrıca onu kodumdaki hemen hemen her yerde kullanmam gerektiğinde kullanacağım ve bir nesneyi, ihtiyacı olan her sınıfa veya işleve parametre olarak geçirmek istemiyorum.

Ek olarak, başka bir işlevin referans şeffaflığını kırmazsa, yalnızca bir singleton kullanacağım. Bazı girdi verilen anlam her zaman aynı çıktıyı üretecektir. Yani onu küresel devlet için kullanmıyorum. Muhtemelen bu küresel durum bir kez başlatılmış ve asla değişmemiş.

Ne zaman kullanmayacağınıza gelince, yukarıdaki 3'e bakın ve bunları tersi yönde değiştirin.


3
-0 ama ben neredeyse -1. Gerçekten tembel olduğumda bunu yapmak için tek bir kod kullanmak yerine kodumda her yere iletmeyi tercih ederim. İsterseniz statik veya statik üye kullanırsam global vars kullanırdım. küresel değişkenler (benim için) singletonlardan çok daha fazla tercih edilir. Globals yalan söyleme
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.