it-swarm.dev

Singleton ne zaman uygundur?

Bazıları Singleton Pattern 'in her zaman bir anti-desen olduğunu düşünmektedir. Ne düşünüyorsun?

67
Fishtoaster

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

  1. Singletonlar daha az yetenekli programcılar tarafından kötüye kullanılır ve kötüye kullanılır ve böylece her şey bir singleton olur ve Class :: get_instance () başvurularıyla kodlanmış kod görürsünüz. Genel olarak, Singleton deseninin kullanımına uygun nitelikte olan yalnızca bir veya iki kaynak (örneğin bir veritabanı bağlantısı gibi) vardır.
  2. Tek tonlar esasen bir veya daha fazla statik yöntem ve özelliğe dayanan statik sınıflardır. Statik olan her şey, Birim Testi yapmaya çalıştığınızda gerçek, elle tutulur problemler oluşturur çünkü bunlar kodunuzda alay edilemeyecek veya saplanamayan çıkmaz noktaları temsil eder. Sonuç olarak, bir Singleton'a (veya başka bir statik yöntem veya sınıfa) dayanan bir sınıfı test ettiğinizde, yalnızca o sınıfı değil, aynı zamanda statik yöntemi veya sınıfı da test edersiniz.

Her ikisinin de bir sonucu olarak, bu sınıfların tek bir örneğini tutmak için geniş bir kapsayıcı nesnesi oluşturmak yaygın bir yaklaşımdır ve yalnızca kapsayıcı nesnesi bu tür sınıfları değiştirirken, diğer birçok sınıfa bunlardan erişim izni verilebilir kapsayıcı nesnesi.

32
Noah Goodrich

Bunun bir anti-desen olduğuna katılıyorum. Neden? Çünkü kodunuzun bağımlılıkları hakkında yalan söylemesine izin verir ve diğer programcıların daha önce değiştirilemeyen tek tonlarınızda değişebilir bir durum getirmemelerine güvenemezsiniz.

Bir sınıf sadece bir dize alan bir kurucuya sahip olabilir, bu yüzden bunun tecrit halinde başlatıldığını ve yan etkileri olmadığını düşünüyorsunuz. Ancak, sessizce, bir tür genel, küresel olarak kullanılabilir tekli nesne ile iletişim kurar, böylece sınıfı her 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 düzgün bir şekilde birim test etmek için, tutarlı test sonuçları elde etmek için teklitondaki mikro durumu yönetmeniz ve küresel durumun farkında olmanız gerekir.

42
Magnus Wolffelt

Singleton modeli temelde sadece tembel olarak başlatılan bir global değişkendir. Küresel değişkenler genellikle ve haklı olarak kötülük olarak kabul edilir, çünkü bir programın görünüşte ilgisiz kısımları arasında uzaktan ürkütücü eyleme izin verirler. Bununla birlikte, IMHO, bir programın başlatma rutininin bir parçası olarak (örneğin, bir yapılandırma dosyasını veya komut satırı bağımsız değişkenlerini okuyarak) bir kez, bir yerden bir kez ayarlanan ve daha sonra sabit olarak işlenen genel değişkenlerle ilgili yanlış bir şey yoktur. Küresel değişkenlerin bu şekilde kullanımı, sadece harften farklıdır, ruhsal olarak değil, derleme zamanında adlandırılmış bir sabitin beyan edilmesinden farklıdır.

Benzer şekilde, Singletons hakkındaki düşüncem, bir programın görünüşte ilgisiz kısımları arasında değişebilir durumu geçmek için kullanıldıklarında kötü olduklarıdır. Değişebilir durum içermiyorlarsa veya içerdikleri değişebilir durum tamamen kapsüllenmişse, nesne kullanıcılarının çok iş parçacıklı bir ortamda bile bu konuda bilgi sahibi olmaları gerekmez.

34
dsimcha

İnsanlar neden kullanıyor?

PHP dünyasında oldukça sayıda singleton gördüm. Gerekçelendirilecek deseni bulduğum herhangi bir kullanım durumunu hatırlamıyorum. Ama bence neden motivasyon hakkında bir fikrim var insanlar bunu kullandılar.

  1. Bekar örnek.

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

    Bu makul bir gerekliliktir, örn. "varsayılan veritabanı bağlantısı" için. Bu, ikinci bir db bağlantısı oluşturmayacağınız anlamına gelmez, sadece varsayılan olarak çalıştığınız anlamına gelir.

  2. Bekar örnekleme.

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

    Bu, yalnızca sınıfın başlatılmasının diğer örneklerle çelişen yan etkileri olması durumunda geçerlidir.

    Bileşen yeniden tasarlanarak genellikle bu çakışmalardan kaçınılabilir - ör. sınıf yapıcısının yan etkilerini ortadan kaldırarak. Veya başka şekillerde çözülebilirler. Ancak yine de bazı yasal kullanım örnekleri olabilir.

    Ayrıca, "tek bir" gereksinimin gerçekten "işlem başına bir" anlamına gelip gelmediğini düşünmelisiniz. Örneğin. kaynak eşzamanlılığı için bu gereksinim, "süreç başına bir" değil, "süreç başına bir bütün sistem" dir. Ve diğer şeyler için bu "uygulama bağlamı" na göre yapılır ve işlem başına sadece bir uygulama içeriğiniz olur.

    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ılan yere götürmek için uygun bir altyapınız yoksa yasaldır. Bu, çerçevenizin veya ortamınızın berbat olduğu anlamına gelebilir, ancak bunu düzeltmek sizin gücünüzde olmayabilir.

    Fiyat sıkı bağlantı, gizli bağımlılıklar ve küresel devlet hakkında kötü olan her şey. Ama muhtemelen bunlardan zaten acı çekiyorsunuzdur.

  4. Tembel örnekleme.

    Bu bir singletonun gerekli bir parçası değil, ama bunları uygulamanın en popüler yolu gibi görünüyor. Ancak, tembel örneklemenin olması güzel bir şey olsa da, bunu başarmak için gerçekten bir singletona ihtiyacınız yoktur.

Tipik uygulama

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

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

Sonuç

Çoğu durumda, aynı sonucu tek birton ve küresel durum olmadan elde edebilirsiniz. Bunun yerine, bağımlılık enjeksiyonu kullanmalısınız ve bağımlılık enjeksiyon kabını düşünmek isteyebilirsiniz.

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

  • Tek örnek (tek örnekleme değil)
  • Global erişim (bronz bir yaş çerçevesiyle çalıştığınız için)
  • Tembel örnekleme (çünkü olması güzeldir)

Yani, bu durumda yapabilecekleriniz:

  • Genel bir kurucu ile, örneklemek istediğiniz bir C sınıfı oluşturun.

  • Statik örnek değişkeni ve tembel örneklemeli statik S :: getInstance () yöntemiyle ayrı bir S sınıfı oluşturun, bu örnek için C sınıfını kullanır.

  • C yapıcısından tüm yan etkileri ortadan kaldırın. Bunun yerine, bu yan etkileri S :: getInstance () yöntemine koyun.

  • Yukarıdaki gereksinimleri içeren birden fazla sınıfınız varsa, sınıf örneklerini küçük bir yerel hizmet kapsayıcısı ile yönetmeyi ve statik örneği yalnızca kapsayıcı için kullanmayı düşünebilirsiniz. Böylece, S :: getContainer () size tembel bir örneklenmiş hizmet kapsayıcısı verir ve kapsayıcıdan diğer nesneleri alırsınız.

  • Mümkün olan yere statik getInstance () yöntemini çağırmaktan kaçının. Mümkün olduğunca bağımlılık enjeksiyonu kullanın. Özellikle, konteyner yaklaşımını birbirine bağlı birden fazla nesne ile kullanırsanız, bunların hiçbirinin S :: getContainer () öğesini çağırması gerekmez.

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

(Buna hala tekil mi diyeceğiz? Bunu yorum bölümüne bırakıyorum ..)

Yararları:

  • Herhangi bir global duruma dokunmadan birim 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 () öğesinin örnek için farklı bir sınıf kullanmasına veya hatta hangi sınıfın kullanılacağını dinamik olarak belirlemesine izin vermek oldukça kolay olacaktır.

7
donquixote

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

Ayrıca ben sadece benim kod hemen hemen her yerde kullanmanız gerektiğinde kullanacağım ve bir nesne veya her sınıf veya işlev gerektiren bir parametre olarak bir nesne geçmek istemiyorum.

Buna ek olarak, yalnızca başka bir işlevin referans şeffaflığını bozmazsa tek birton kullanacağım. Bazı girdiler göz önüne alındığında her zaman aynı çıktıyı üretecektir. Yani Küresel devlet için kullanmıyorum. Büyük olasılıkla küresel devlet bir kez başlatılır ve asla değiştirilmez.

Ne zaman kullanılmayacağına gelince, yukarıdaki 3'e bakın ve bunları tersine değiştirin.

1
Brian R. Bondy