Silnik wykorzystuje już wiele singletonów. Jeśli ktoś go użył, powinien znać niektóre z nich:
Nieznajomość nie jest powodem, dla którego należy unikać singletonów.
Istnieje wiele dobrych powodów, dla których Singleton jest konieczny lub nieunikniony. Ramy gier często używają singletonów, ponieważ jest to nieunikniona konsekwencja posiadania tylko jednego stanowego sprzętu. Nie ma sensu nigdy kontrolować tych programów za pomocą wielu instancji odpowiednich programów obsługi. Powierzchnia graficzna jest zewnętrznym stanowym sprzętem i przypadkowe zainicjowanie drugiej kopii podsystemu graficznego jest katastrofalne, ponieważ teraz oba podsystemy graficzne będą ze sobą walczyć o to, kto będzie rysował i kiedy, nadpisując się w niekontrolowany sposób. Podobnie z systemem kolejek zdarzeń będą walczyć o to, kto otrzyma zdarzenia myszy w nieokreślony sposób. W przypadku stanowego sprzętu zewnętrznego, w którym jest tylko jeden, singleton jest nieunikniony, aby zapobiec konfliktom.
Innym miejscem, w którym Singleton jest rozsądny, są menedżerowie Cache. Skrytki są szczególnym przypadkiem. Nie należy ich tak naprawdę uważać za singletonów, nawet jeśli używają tych samych technik co singletony, aby pozostać przy życiu i żyć wiecznie. Usługi buforowania są usługą przezroczystą, nie powinny zmieniać zachowania programu, więc jeśli zastąpisz usługę pamięci podręcznej zerową pamięcią podręczną, program powinien nadal działać, z wyjątkiem tego, że działa tylko wolniej. Głównym powodem, dla którego menedżerowie pamięci podręcznej są wyjątkiem od singletonu, jest to, że nie ma sensu zamykać usługi pamięci podręcznej przed zamknięciem samej aplikacji, ponieważ spowoduje to również odrzucenie buforowanych obiektów, co nie pozwala na to, aby była ona singel.
Są to dobre powody, dla których usługi te są singletonami. Jednak żaden z dobrych powodów, aby mieć singletony, nie dotyczy wymienionych klas.
Ponieważ będę ich potrzebować w bardzo różnych miejscach w mojej grze, a wspólny dostęp byłby bardzo przydatny.
To nie są powody do singletonów. To są powody globalizacji. Jest to również oznaką złego projektu, jeśli musisz przekazać wiele rzeczy wokół różnych systemów. Konieczność przekazywania rzeczy wskazuje na wysokie sprzężenie, któremu można zapobiec dzięki dobrej konstrukcji OO.
Wystarczy spojrzeć na listę klas w twoim poście, które Twoim zdaniem powinny być singletonami, mogę powiedzieć, że połowa z nich tak naprawdę nie powinna być singletonami, a druga połowa wydaje się, że nie powinny tam być. To, że musisz przekazywać obiekty, wydaje się wynikać z braku właściwej enkapsulacji, a nie z rzeczywistych dobrych przypadków użycia singletonów.
Spójrzmy na twoje klasy krok po kroku:
PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)
Po prostu z nazw klas powiedziałbym, że klasy te pachną jak antymateriał Anemic Domain Model . Obiekty anemiczne zwykle powodują przekazanie dużej ilości danych, co zwiększa sprzężenie i sprawia, że reszta kodu jest niewygodna. Ponadto klasa anemiczna ukrywa fakt, że prawdopodobnie nadal myślisz proceduralnie, zamiast używać orientacji obiektowej do kapsułkowania szczegółów.
IAP (for in purchases)
Ads (for showing ads)
Dlaczego te klasy muszą być singletonami? Wydaje mi się, że powinny to być zajęcia krótkotrwałe, które należy wychowywać, gdy są potrzebne, i dekonstruować je, gdy użytkownik zakończy zakup lub gdy reklamy nie będą już wyświetlane.
EntityComponentSystemManager (mananges entity creation and manipulation)
Innymi słowy, konstruktor i warstwa usługi? Dlaczego w ogóle ta klasa nie ma wyraźnych granic ani celu?
PlayerProgress (passed levels, stars)
Dlaczego postęp gracza jest oddzielony od klasy gracza? Klasa Player powinna wiedzieć, jak śledzić swoje postępy, jeśli chcesz wdrożyć śledzenie postępu w innej klasie niż klasa Player w celu rozdzielenia odpowiedzialności, to PlayerProgress powinien stać za Graczem.
Box2dManager (manages the physics world)
Naprawdę nie mogę komentować tego dalej, nie wiedząc, co właściwie robi ta klasa, ale jedno jest jasne, że ta klasa jest źle nazwana.
Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
Wydaje się, że są to jedyne klasy, w których Singleton może być uzasadniony, ponieważ obiekty połączenia społecznego nie są w rzeczywistości obiektami. Połączenia społecznościowe to tylko cień zewnętrznych obiektów, które żyją w zdalnej usłudze. Innymi słowy, jest to rodzaj pamięci podręcznej. Upewnij się tylko, że wyraźnie oddzieliłeś część buforującą od części serwisowej usługi zewnętrznej, ponieważ ta ostatnia część nie powinna być singletonem.
Jednym ze sposobów uniknięcia przekazywania instancji tych klas jest użycie przekazywania wiadomości. Zamiast nawiązywać bezpośrednie połączenia z instancjami tych klas, zamiast tego wysyłasz wiadomość skierowaną do usługi analitycznej i usługi SocialConnection asynchronicznie, a usługi te są subskrybowane, aby otrzymywać te wiadomości i działać na podstawie wiadomości. Ponieważ kolejka zdarzeń jest już singletonem, trampolując połączenie, unikniesz konieczności przekazywania rzeczywistej instancji podczas komunikacji z singletonem.