środa, 3 listopada 2010

Remont silnika odpowiedź

Anonimowy użytkownik zapytał czemu w poście Remont silnika wyraziłem nie chęć do stosowania rozwiązań typu singleton "Czemu zero rozwiązań typu singleton? Globalny, jednolity stan w jakiejkolwiek formie (monostate, singleton, zwykła klasa statyczna), jest bardzo naturalny i nie ma sensu unikać go na siłę w imię jakichś pustycznych idei."

Odpowiedź jest dość obszerna, także umieszczę to w nowym wpisie.

Moim zdaniem największym problemem z wszelkiej maści obiektami o dostępie globalnym są zależności jakie powstają pomiędzy różnymi modułami oprogramowania.
Pisząc swój silnik graficzny byłem bardzo pozytywnie nastawiony do tego typu rozwiązań, bo "ułatwiały" mi życie. Dzięki nim mogłem zmniejszyć ilość parametrów przekazywanych do metod. Unikałem również przekazywania obiektów w głąb hierarchii wywołań. (chcemy zasób, więc przekazujemy managerowi obiekt api, ten przekazuje go fabryce, ta przekazuje go konkretnej metodzie). Kto potrzebuje singletona ten sam może go sobie wziąć. I tak właśnie powstają zależności pomiędzy jednym modułem, a drugim. Przykładowo klasa tekstury potrzebowała rozmiar okna dla którego będzie render targetem. Bach, okno jest singletonem, wszyscy maja do niego dostęp. To był chyba największy błąd projektowy jaki popełniłem.

Zależności się nie skalują. Zmiana w jednym module często pociąga ogromne zmiany w całym systemie.
Zależności się bardzo szybko rozrastają. Ja w końcu się pogubiłem w kolejności otwierania i zamykania modułów. We własnym kodzie straciłem orientację.
Zależności się nie skalują. Jak zapewnić rozsądny dostęp do globalnych obiektów w środowisku wielowątkowym?
Obiekty globalne gryzą się z raii. Obiekt globalny jakim jest std::clog jest bardzo wygodny kiedy potrzebujemy coś logować. Osobiście bardzo często z niego korzystam, w taki sposób, że podpinam pod niego plik.
W main robie sobie taki trick:
fstream log("log.log" fstream::out);
clog.rdbuf(log.rdbuf());
Potem w dowolnym miejscu kodu wystarczy zapisać do std::clog i wszystko trafi do pliku.
Informacją, którą szczególnie warto logować jest moment otwierania i zamykania poszczególnych modułów. Obiekt zarządzający modułem powstanie już po otwarciu pliku, więc log powstanie poprawnie. Jeżeli jednak pozwolimy istnieć takiemu obiektowi poza czas trwania maina to mamy problem. Ja nie raz popełniłem taki błąd, nie tylko przy projekcie tego silnika. Niby wiem, że tak jest, a i tak wciąż popełniam ten błąd.

2 komentarze:

Unknown pisze...

Cóż, wszystko zależy od człowieka :) dla mnie singletony dobrze użyte są niesamowicie wygodne i nie sprawiają mi problemów. Ale jest to faktycznie coś takiego, że jedni je uwielbiają, inni nienawidzą :)

Adam Śmigielski pisze...

Do nie nienawiści mi bardzo daleko ;) Zwyczajnie doszedłem do wniosku, że tam gdzie się da, lepiej jest przekazać obiekt przez parametr.

Tylko tam, gdzie mi się nie uda zostaną zastosowane obiekty globalne.