Singletons / globals'e alternatifler


16

Singletons / globallerin tuzakları hakkında sayısız kez duydum ve neden bu kadar sık ​​sık kaşlarını çattıklarını anlıyorum.

Anlamadığım şey zarif, dağınık olmayan alternatifin ne olduğudur. Singletons / globals kullanmanın alternatifi, nesneleri her zaman, ihtiyaç duyulan nesnelere ulaşana kadar motor nesnelerinizden bir milyon seviye aşağı geçmeyi içerir.

Örneğin, oyunumda, oyun başladığında bazı varlıkları önceden yüklüyorum. Bu varlıklar, oyuncu ana menüde gezinip oyuna girene kadar kullanılmaz. Bu verileri Oyun nesnesimden, ScreenManager nesnesime (yalnızca bir Ekranın bu verileri gerçekten önemsediği gerçeğine rağmen), sonra uygun Ekran nesnesine ve başka bir yere mi geçirmem gerekiyor?

Görünüşe göre, küresel durum verilerini karmaşık bağımlılık enjeksiyonu için alıyorum, verileri alt nesnelere aktarmak dışında verileri umursamayan nesnelere bile veriyorum.

Bu bir Singleton'ın iyi bir şey olacağı bir durum mu yoksa eksik olduğum bazı zarif çözümler var mı?

Yanıtlar:


16

Tekilleri ve küreselleri birleştirmeyin. Bir tür küresel değişken genellikle gerekli olsa da, singleton sadece global bir değişkenin yerini almakla kalmaz , aynı zamanda C ++ ( ve FQA ) 'da statik başlatma düzenindeki problemleri çözmenin bir yoludur . (Diğer dillerde, küresel değişkenlerin ve çıplak işlevlerin eksikliği gibi farklı dil eksikliklerini çözmenin bir yoludur.)

Tek birton yerine global bir işaretçi kullanabilir ve herhangi bir şeye ihtiyaç duymadan önce (manuel olarak) başlatıldığından emin olursanız, işlev çağrısı ve şube ek yükünden, nesneye ulaşmak için topal sözdiziminden kaçınırsınız ve aslında bir testlere ihtiyaç duyduğunuzda veya tasarımınız değiştiği için sınıfın ikinci örneği.

İstediğiniz az sayıda global değişken için (yaygın örnekler ses çıkışı, açık pencerelerin listesi, klavye işleyici vb.), Servis bulucu desenini öneririm . İşleri farklı uygulamalarla değiştirmeyi kolaylaştırır (örn. Gerçek ve null ses aygıtı) ve ad alanınızı kirletmemek için tüm globalleri tek bir yapıda toplar.


+1. İyi cevap ve servis bulucu desen bağlantısı için teşekkürler. Bu çok ilginç bir okuma.
bummzack

1

Kodun bir kısmına bazı veriler hakkında sihirli bir şekilde "bilmek" yoksa / yapamıyorsanız, bir şekilde iletilmesi gerekir. Ancak bu, yalnızca argümanlardan geçilmesi gerektiği anlamına gelmez.

Örnek durumunuzda, varlıkları yükleyecek ve depolayacak bir tür "AssetManager" a sahip olamazdınız ve ardından ScreenManager'a sadece bir referans verilmelidir (muhtemelen yaratılışta)? Bu anlamda referansları başka bir nesneye sarılmış varlıklara aktarıyorsunuz ve bunu gerektiğinde yaprak işlevine aktarmak yerine bir kez, başlatma sırasında iletebilirsiniz.

Şimdi IMHO, AssetManager'ın sadece birini istediğin türden biri olarak tekil olabilir. Tuzakları anlamanız ve bunlardan kaçınmak için özel olarak kodlamanız şartıyla (singleton'a birden çok iş parçacığından aynı anda erişileceğini ve engellenmesi gereken bir şey yaptığınızda her zaman bir çatalla bıçaklayacağınızı varsayalım), sonra kendinizi eleyin.


1

Bence Jason D kesinlikle haklı - ben bu şekilde halledeceğim:

Oyun, herhangi bir varlığı ada göre alabileceğiniz bir nesne olan AssetManager'ın bir örneğine sahiptir.

Oyunda:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

ScreenManager'da:

screen = new Screen();
screen.assetManager = assetManager;

Ekranda:

myAsset = assetManager.getBitmp("lava.png");

Artık tüm ekranlar ihtiyaç duydukları varlıklara erişebiliyor. Bu globals veya Singletons kullanmaktan daha karmaşık veya çılgın değildir ve aynı uygulamada çatışma olmadan 2 Oyun örneğine sahip olma seçeneğiniz vardır. Bir zamanlar hepsi aynı temel sınıfları / çerçeveyi paylaşan 8 mini oyundan oluşan bir oyun yapmak zorunda kaldım. Bu referans geçiş stilini kullanmak için tüm globalleri / singletonlarımı yeniden düzenlemek zorunda kaldım ve asla geriye bakmadım. Küresel olması gereken tek şey, ses, ağ, g / Ç vb. Gibi yalnızca bir kez fiziksel olarak var olabilecek şeylerdir.


0

Sen kullanabilirsiniz Singleton yerine Fabrika deseni . Daha sonra fabrika sınıfı, kaç tane örneğe ihtiyacınız olduğunu bulduğunuzda daha sonra kolayca değiştirebileceğiniz kaç tane örnek oluşturabileceğiniz üzerinde kontrole sahiptir AssetManager. Bu makalede belirtildiği gibi :

Size Singleton'un tüm esnekliklerini verir, hiçbir yerde bu kadar fazla soruna yakın değildir.


Başka bir, oldukça sınırlı bir olasılık sınıf statik yapmak (ki bir AssetManager için uygulanabilir olduğunu ve sadece statik sınıfları olan tüm dillerde mümkün olduğunu düşünüyorum). Ancak bu sadece kalıtım / polimorfizme ihtiyacınız yoksa işe yarar. Çok esnek olmayan bir çözüm:

statik yöntemler granit kadar esnektir. Birini her kullandığınızda, programınızın bir kısmını betona döküyorsunuz. Sertleşmesini izlerken ayağınızın orada sıkışmamasına dikkat edin. Bir gün, tanrıyla, bu dang PrintSpooler sınıfının başka bir uygulamasına gerçekten ihtiyaç duyduğuna şaşıracaksınız ve bir arayüz, bir fabrika ve bir dizi uygulama sınıfı olmalı. D'oh!

Bu statik yöntemlerle ilgilidir, ancak statik sınıflara da uygulanabilir.


"Sınıfı statik hale getirmek" ile ne demek istiyorsun? C ++ statik sınıflara sahip değildir. Sadece statik yöntemleriniz mi var? Öyleyse neden bir ad alanı yerine bir sınıfa uğraşasınız ki?

1
@Joe: Anladığım kadarıyla soru C ++ üzerine odaklanmadı. C # veya Java'da statik sınıflar yapabilirsiniz ve bunlara atıfta bulunuyorum. Ayrıca, dediğim gibi, statik sınıflar çoğu zaman en uygun çözüm değildir, ancak nadir durumlarda küresel gibi çalışabilir.
Michael Klement
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.