Słyszałem niezliczoną ilość razy o pułapkach Singletonów / globali i rozumiem, dlaczego tak często się im nie podobają.
Nie rozumiem, czym jest elegancka, nieuporządkowana alternatywa. Wydaje się, że alternatywa dla korzystania z singletonów / globałów zawsze polega na przepuszczaniu obiektów o milion poziomów w dół przez obiekty silnika, aż dotrą one do obiektów, które ich potrzebują.
Na przykład w mojej grze wstępnie ładuję niektóre zasoby, gdy gra się uruchamia. Zasoby te są wykorzystywane dopiero później, gdy gracz porusza się po menu głównym i wchodzi do gry. Czy mam przekazać te dane z mojego obiektu gry do mojego obiektu ScreenManager (pomimo faktu, że tylko jeden ekran faktycznie dba o te dane), a następnie do odpowiedniego obiektu ekranu i gdziekolwiek indziej?
Wydaje się po prostu, że handluję danymi o stanie globalnym w celu wstrzykiwania zaśmieconej zależności, przekazując dane do obiektów, które nawet nie dbają o dane, z wyjątkiem celu przekazania ich do obiektów potomnych.
Czy to przypadek, w którym Singleton byłby dobrą rzeczą, czy jest jakieś eleganckie rozwiązanie, którego mi brakuje?
źródło
Jeśli nie masz / nie możesz mieć części kodu w magiczny sposób „wiedzący” o niektórych danych, to trzeba będzie jakoś przekazać. Nie oznacza to jednak, że należy koniecznie przekazywać tylko argumenty.
W twoim przykładzie, czy nie mógłbyś mieć jakiegoś „AssetManagera”, który ładowałby i przechowywał zasoby, a wtedy ScreenManager musiałby tylko mieć odniesienie do tego (prawdopodobnie podczas tworzenia)? W tym sensie przekazujesz referencje do zasobów opakowanych w inny obiekt i możesz to przekazać raz podczas inicjalizacji, zamiast przekazywać to do funkcji liścia, gdy jest to wymagane.
Teraz IMHO, że AssetManager, będący czymś, czego chcesz tylko jeden, może równie dobrze być singletonem. Pod warunkiem, że rozumiesz pułapki i kod, aby ich uniknąć (załóż, że singleton będzie dostępny jednocześnie z wielu wątków i dźgnij się widelcem za każdym razem, gdy zrobisz coś, co musi zostać zablokowane), a następnie powal się.
źródło
Myślę, że Jason D ma absolutną rację - tak bym sobie z tym poradził:
Gra ma instancję AssetManager, obiekt, z którego można uzyskać dowolny zasób według nazwy.
W grze:
W ScreenManager:
Na ekranie:
Teraz wszystkie ekrany mają dostęp do potrzebnych zasobów. Nie jest to nic bardziej złożonego ani szalonego niż używanie globali lub singletonów, a masz możliwość uruchomienia 2 instancji Gry w tej samej aplikacji bez kolizji. Kiedyś musiałem stworzyć grę składającą się z 8 mini-gier, wszystkie dzielące te same klasy podstawowe / framework. Musiałem zmienić wszystkie moje globale / singletony, aby użyć tego stylu przekazywania referencji, i nigdy nie oglądałem się za siebie. Jedynymi rzeczami, które powinny być globalne, są rzeczy, które mogą fizycznie istnieć tylko raz, takie jak audio, sieci, we / wy itp.
źródło
Możesz użyć wzorca fabrycznego, aby zastąpić Singletona . Następnie klasa fabryczna ma kontrolę nad tym, ile wystąpień możesz utworzyć, które możesz łatwo zmienić później, gdy okaże się, że potrzebujesz więcej niż jednego
AssetManager
. Jak stwierdzono w tym artykule :Inną, raczej ograniczoną, możliwością jest uczynienie klasy statyczną (co nie uważam za wykonalne dla AssetManager i możliwe tylko w językach, które w ogóle mają klasy statyczne). Ale działa to tylko wtedy, gdy nie potrzebujesz dziedziczenia / polimorfizmu. To bardzo nieelastyczne rozwiązanie:
Chodzi o metody statyczne, ale można je również zastosować do klas statycznych.
źródło