Bazıları Singleton Paterninin her zaman bir anti patern olduğunu belirtir . Ne düşünüyorsun?
Bazıları Singleton Paterninin her zaman bir anti patern olduğunu belirtir . Ne düşünüyorsun?
Yanıtlar:
Singletons'ın iki ana eleştirisi, gözlemlediğimden iki kampa düşüyor:
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.
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.
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.
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.
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.
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.
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.
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, ö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 .
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:
İş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.
Ş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.