Mam nadzieję, że te kłótnie wyjaśnią moje pytanie - jednak całkowicie zrozumiem, jeśli nie, więc daj mi znać, jeśli tak jest, a ja postaram się wyjaśnić.
Poznaj BoxPong , bardzo prostą grę, którą stworzyłem, aby zapoznać się z tworzeniem gier obiektowych. Przeciągnij pole, aby kontrolować piłkę i zbierać żółte rzeczy.
Tworzenie BoxPong pomogło mi sformułować między innymi podstawowe pytanie: w jaki sposób mogę mieć obiekty, które współdziałają ze sobą bez konieczności „przynależenia” do siebie? Innymi słowy, czy istnieje sposób, aby obiekty nie były hierarchiczne, ale współistniały? (Przejdę do dalszych szczegółów poniżej.)
Podejrzewam, że problem współistnienia obiektów jest powszechny, więc mam nadzieję, że istnieje ustalony sposób jego rozwiązania. Nie chcę wymyślać kwadratowego koła, więc wydaje mi się, że idealną odpowiedzią, której szukam, jest „oto wzór projektowy, który jest często używany do rozwiązania twojego rodzaju problemu”.
Zwłaszcza w prostych grach, takich jak BoxPong, jasne jest, że istnieje lub powinna istnieć garść przedmiotów współistniejących na tym samym poziomie. Jest pudełko, piłka, jest przedmiot kolekcjonerski. Wszystko, co mogę wyrazić w językach obiektowych, chociaż - a przynajmniej tak się wydaje - to ścisłe relacje HAS-A . Odbywa się to poprzez zmienne składowe. Nie mogę tak po prostu zacząć ball
i pozwolić, by zrobiła to samo, potrzebuję, aby na stałe przynależeć do innego obiektu. Mam go skonfigurować tak, że głównym celem gry jest okno, a okno z kolei ma piłkę, a ma licznik wynik. Każdy obiekt ma równieżupdate()
metoda, która oblicza pozycję, kierunek itp., i idę tam podobną drogą: wywołuję metodę aktualizacji głównego obiektu gry, która wywołuje metody aktualizacji wszystkich jej dzieci, a one z kolei wywołują metody aktualizacji wszystkich swoich dzieci. To jedyny sposób, w jaki mogę stworzyć grę obiektową, ale uważam, że nie jest to idealny sposób. W końcu nie uważałbym piłki za należącą do pudełka, ale raczej za bycie na tym samym poziomie i interakcję z nią. Przypuszczam, że można to osiągnąć, zamieniając wszystkie obiekty gry w zmienne składowe głównego obiektu gry, ale nie widzę, żeby to rozwiązywało. Mam na myśli ... pomijając oczywisty bałagan, w jaki sposób piłka i pudełko mogłyby się poznać , to znaczy wchodzić w interakcje?
Istnieje również problem obiektów, które muszą przekazywać między sobą informacje. Mam spore doświadczenie w pisaniu kodu do SNES, gdzie masz dostęp do praktycznie całej pamięci RAM przez cały czas. Załóżmy, że tworzysz niestandardowego wroga dla Super Mario World , i chcesz, aby usunął wszystkie monety Mario, a następnie po prostu zapisz zero, aby rozwiązać problem 0 USD bez problemu. Nie ma żadnych ograniczeń, że wrogowie nie mogą uzyskać dostępu do statusu gracza. Wydaje mi się, że ta wolność mnie rozpieszcza, ponieważ w C ++ i podobnych często zastanawiam się, jak udostępnić wartość innym obiektom (a nawet globalnym).
Na przykładzie BoxPong, co jeśli chciałbym, aby piłka odbiła się od krawędzi ekranu? width
i height
są właściwościami Game
klasy,ball
mieć do nich dostęp. Mógłbym przekazywać tego rodzaju wartości (za pomocą konstruktorów lub metod, w których są potrzebne), ale to po prostu krzyczy mi złe praktyki.
Wydaje mi się, że moim głównym problemem jest to, że potrzebuję przedmiotów, aby się poznać, ale jedynym sposobem, w jaki to widzę, jest ścisła hierarchia, która jest brzydka i niepraktyczna.
Słyszałem o „klasach przyjaciół” w C ++ i trochę wiem, jak one działają, ale jeśli są one rozwiązaniem całościowym, to dlaczego nie widzę friend
słów kluczowych wylanych w każdym projekcie C ++ i dlaczego koncepcja nie istnieje w każdym języku OOP? (To samo dotyczy wskaźników funkcji, o których niedawno się dowiedziałem.)
Z góry dziękuję za odpowiedzi dowolnego rodzaju - i ponownie, jeśli jest jakaś część, która nie ma dla ciebie sensu, daj mi znać.
Odpowiedzi:
Zasadniczo okazuje się, że obiekty tego samego poziomu znają się bardzo źle. Gdy obiekty się dowiedzą, zostaną ze sobą powiązane lub połączone . To sprawia, że są trudne do zmiany, trudne do przetestowania, trudne do utrzymania.
Działa to znacznie lepiej, jeśli istnieje jakiś obiekt „powyżej”, który wie o tych dwóch i może ustawić interakcje między nimi. Obiekt, który wie o dwóch elementach równorzędnych, może powiązać je ze sobą poprzez wstrzyknięcie zależności lub zdarzenia lub przekazanie wiadomości (lub dowolny inny mechanizm odsprzęgający). Tak, to prowadzi do trochę sztucznej hierarchii, ale jest o wiele lepsze niż bałagan spaghetti, który pojawia się, gdy rzeczy po prostu chcą się rozproszyć. Jest to tylko ważniejsze w C ++, ponieważ potrzebujesz czegoś, aby mieć również żywotność obiektów.
Krótko mówiąc, możesz to zrobić, łącząc obiekty obok siebie przez dostęp ad hoc, ale to zły pomysł. Hierarchia zapewnia porządek i przejrzystą własność. Najważniejszą rzeczą do zapamiętania jest to, że obiekty w kodzie niekoniecznie są obiektami w prawdziwym życiu (a nawet w grze). Jeśli obiekty w grze nie tworzą dobrej hierarchii, lepsza może być inna abstrakcja.
źródło
Nie!
Myślę, że głównym problemem, jaki masz, jest zbyt „dosłowne” podejście do programowania obiektowego. W OOP obiekt nie reprezentuje „rzeczy”, ale „ideę”, co oznacza, że „piłka”, „gra”, „fizyka”, „matematyka”, „data” itd. Wszystkie są prawidłowymi obiektami. Nie ma również wymogu, aby obiekty „wiedziały” o czymkolwiek. Na przykład
Date.Now().getTommorrow()
zapytałby komputer, który jest dzień, zastosował tajemne reguły dotyczące dat, aby ustalić datę jutra i zwrócił ją dzwoniącemu.Date
Obiekt nie wie o cokolwiek innego, potrzebuje tylko informacji żądania, ile potrzeba z systemu. Ponadto,Math.SquareRoot(number)
nie trzeba nic wiedzieć poza logiką jak obliczyć pierwiastek kwadratowy.W twoim przykładzie, który zacytowałem, „Piłka” nie powinna wiedzieć nic o „Boksie”. Pudełka i kulki to zupełnie inne pomysły i nie mają prawa ze sobą rozmawiać. Ale silnik fizyki wie, co to jest Box and Ball (a przynajmniej ThreeDShape) i wie, gdzie się znajdują i co się z nimi dzieje. Więc jeśli piłka kurczy się, ponieważ jest zimna, silnik fizyki powiedziałby instancji piłki, że jest teraz mniejsza.
To trochę jak budowanie samochodu. Chip komputerowy nic nie wie o silniku samochodowym, ale samochód może użyć chipu komputerowego do sterowania silnikiem. Prosty pomysł użycia małych, prostych rzeczy razem, aby stworzyć nieco większą, bardziej złożoną rzecz, która sama w sobie może być używana jako komponent innych, bardziej złożonych części.
A w przykładzie Mario, co jeśli jesteś w pokoju wyzwań, w którym dotknięcie wroga nie powoduje marnowania monet Marios, a jedynie wyrzuca go z tego pokoju? Jest poza obszarem idei Mario lub wroga, że Mario powinien stracić monety, dotykając wroga (w rzeczywistości, jeśli Mario ma gwiazdę niewrażliwości, zabija wroga zamiast tego). Tak więc każdy obiekt (dziedzina / idea), który jest odpowiedzialny za to, co dzieje się, gdy Mario dotyka wroga, jest jedynym, który musi o nim wiedzieć i powinien zrobić cokolwiek z nim (w zakresie, w jakim obiekt ten pozwala na zewnętrzne zmiany sterowane ).
Co więcej, w twoich wypowiedziach na temat obiektów wywołujących dzieci
Update()
, jest to bardzo podatne na błędy, jak gdybyUpdate
nazywało się wiele razy na ramkę od różnych rodziców? (nawet jeśli złapiesz to, to zmarnowany czas procesora, który może spowolnić twoją grę) Każdy powinien dotykać tylko tego, czego potrzebuje, kiedy jest to konieczne. Jeśli używasz Update (), powinieneś użyć jakiejś formy wzorca subskrypcji, aby upewnić się, że wszystkie Aktualizacje są wywoływane raz na ramkę (jeśli nie jest to obsługiwane w Unity)Nauka definiowania pomysłów domeny w jasne, izolowane, dobrze zdefiniowane i łatwe w użyciu bloki będzie największym czynnikiem wpływającym na efektywność wykorzystania OOP.
źródło
Wygląda na to, że brakuje ci programowania obiektowego.
Programowanie obiektowe polega na zarządzaniu zależnościami poprzez selektywne odwracanie niektórych kluczowych zależności w architekturze, aby zapobiec sztywności, kruchości i niemożliwości ponownego użycia.
Czym jest zależność? Zależność polega na czymś innym. Kiedy przechowujesz zero, aby zaadresować 0DBF, polegasz na tym, że pod tym adresem znajdują się monety Mario i że monety są reprezentowane jako liczba całkowita. Twój niestandardowy kod wroga zależy od kodu implementującego Mario i jego monety. Jeśli zmienisz miejsce, w którym Mario przechowuje swoje monety w pamięci, musisz ręcznie zaktualizować cały kod odnoszący się do lokalizacji w pamięci.
Kod zorientowany obiektowo polega na uzależnieniu kodu od abstrakcji, a nie od szczegółów. Więc zamiast
piszesz
Teraz, jeśli chcesz zmienić sposób, w jaki Mario przechowuje swoje monety z długiego lub podwójnego, lub przechowuje je w sieci, przechowuje w bazie danych lub rozpoczyna inny długi proces, dokonujesz zmiany w jednym miejscu: Klasa Mario i cały pozostały kod działa bez żadnych zmian.
Dlatego kiedy pytasz
naprawdę pytasz:
który nie jest programowaniem obiektowym.
Sugeruję, aby zacząć od przeczytania wszystkiego tutaj: http://objectmentor.com/omSolutions/oops_what.html, a następnie wyszukania wszystkiego na YouTubie przez Roberta Martina i obejrzenia wszystkiego.
Moje odpowiedzi pochodzą od niego, a niektóre z nich są bezpośrednio od niego cytowane.
źródło
mario.coins = 0;
, alemario.loseCoins();
co jest w porządku i prawda - ale chodzi mi o to, w jaki sposób wróg możemario
mimo to uzyskać dostęp do obiektu?mario
bycie zmienną członkowskąenemy
nie wydaje mi się właściwe.Możesz włączyć luźne sprzęganie, stosując wzór mediatora. Jego wdrożenie wymaga odniesienia do komponentu mediatora, który zna wszystkie komponenty odbierające.
Uświadamia sobie „myśl mistrza gry, proszę, niech tak się stanie”.
Ogólny wzorzec to wzorzec publikowania-subskrybowania. Właściwe jest, aby mediator nie zawierał dużej logiki. W przeciwnym razie użyj ręcznie spreparowanego mediatora, który wie, gdzie kierować wszystkie połączenia, a może nawet je modyfikować.
Istnieją warianty synchroniczne i asynchroniczne, zwykle nazywane magistralą zdarzeń, magistralą komunikatów lub kolejką komunikatów. Sprawdź je, aby ustalić, czy są odpowiednie w danym przypadku.
źródło