Grupa przyjaciół i ja pracowaliśmy od jakiegoś czasu nad projektem i chcieliśmy wymyślić przyjemny sposób OOP reprezentujący scenariusz specyficzny dla naszego produktu. Zasadniczo pracujemy nad grą w piekło w stylu Touhou i chcieliśmy stworzyć system, w którym moglibyśmy z łatwością reprezentować każde możliwe zachowanie kuli, jakie moglibyśmy wymarzyć.
Tak właśnie zrobiliśmy; stworzyliśmy naprawdę elegancką architekturę, która pozwoliła nam podzielić zachowanie pocisku na różne komponenty, które mogą być dowolnie dołączane do instancji pocisków, podobnie jak system komponentów Unity . Działał ładnie, był łatwo rozszerzalny, był elastyczny i obejmował wszystkie nasze bazy, ale był niewielki problem.
Nasza aplikacja obejmuje również dużą liczbę generowania procedur, a mianowicie generujemy proceduralnie zachowania pocisków. Dlaczego to jest problem? Cóż, nasze rozwiązanie OOP do reprezentowania zachowania pocisków, choć eleganckie, jest nieco skomplikowane w pracy bez człowieka. Ludzie są wystarczająco inteligentni, aby wymyślić rozwiązania problemów, które są zarówno logiczne, jak i sprytne. Algorytmy generowania procedur nie są jeszcze tak sprytne i trudno nam było wdrożyć sztuczną inteligencję, która w pełni wykorzystuje naszą architekturę OOP. Trzeba przyznać, że wadą architektury jest to, że nie jest ona intuicyjna we wszystkich sytuacjach.
Aby rozwiązać ten problem, w zasadzie wepchnęliśmy wszystkie zachowania oferowane przez różne komponenty do klasy wypunktowanej, aby wszystko, co mogliśmy sobie wyobrazić, było oferowane bezpośrednio w każdej instancji wypunktowanej, w przeciwieństwie do innych powiązanych instancji komponentowych. To sprawia, że nasze algorytmy generowania procedur są nieco łatwiejsze w obsłudze, ale teraz nasza klasa pocisków jest ogromnym bogiem . Jest to jak dotąd największa klasa w programie z ponad pięciokrotnie większym kodem niż cokolwiek innego. Utrzymanie jest również trochę uciążliwe.
Czy to w porządku, że jedna z naszych klas zamieniła się w obiekt boski, aby ułatwić pracę z innym problemem? Zasadniczo, czy jest w porządku mieć zapach w kodzie, jeśli pozwala on na łatwiejsze rozwiązanie innego problemu?
źródło
Odpowiedzi:
Podczas tworzenia rzeczywistych programów często dochodzi do kompromisu między pragmatyzmem z jednej strony a utrzymaniem 100% czystości z drugiej. Jeśli utrzymanie czystości zabrania ci terminowego wysłania produktu, lepiej jest użyć trochę taśmy klejącej, aby wydostać się z domu.
Powiedziałeś, że twój opis brzmi inaczej - brzmi, że nie zamierzasz dodać trochę taśmy izolacyjnej, brzmi to tak, jakbyś zniszczył całą architekturę, ponieważ nie wyglądałeś wystarczająco długo i wystarczająco mocno, aby uzyskać lepsze rozwiązanie. Zamiast szukać kogoś tutaj na PSE, który da ci błogosławieństwo, lepiej zadaj inne pytanie, w którym opisujesz niektóre szczegóły problemów, które masz dogłębnie, i sprawdź, czy ktoś oferuje ci pomysł, który omija boga podejście klasowe.
Być może klasa pocisku może być zaprojektowana jako fasada wielu innych klas, więc klasa pocisku staje się mniejsza. Może wzorzec strategii może pomóc, aby pocisk mógł przekazać różne zachowania różnym obiektom strategii. Być może potrzebujesz tylko przejściówki między komponentem pocisku a generatorem procedur. Ale szczerze mówiąc, nie znając więcej szczegółów twojego systemu, można tylko zgadywać.
źródło
Interesujące pytanie. Jestem jednak trochę stronniczy z powodu moich wcześniejszych doświadczeń, co skłania mnie do odpowiedzi na nie.
Krótka odpowiedź: nigdy nie przestajemy się uczyć. Kiedy uderzysz w taką ścianę, jest to szansa na poprawę twoich umiejętności architektonicznych / projektowych, a nie pretekst do dodawania zapachów kodu.
Dłuższa wersja jest taka, że często zadawano mi podobne pytania w moich projektach w pracy, ale nieuchronnie skończyliśmy na omawianiu problemu bardziej szczegółowo, niż pierwotny pytający udzielił mu uznania. Chcesz uwzględnić w tym mentalności, takie jak analiza przyczyn źródłowych.
Uważam, że 5 Whys szczególnie pomocne. Twoje wyjaśnienie brzmi tak samo jak pierwsze, dlaczego:
Co to dokładnie jest uproszczone? A dlaczego tak jest? Postępuj zgodnie z tą linią, a to, co zwykle się dzieje, polega na tym, że zaczynasz rozpoznawać znacznie bardziej fundamentalne problemy, ale jednocześnie pozwalają one również na bardziej podstawowe rozwiązania.
Krótko mówiąc, po głębszym zastanowieniu się nad problemem, a zwłaszcza jego przyczynami, z mojego doświadczenia wynika, że pierwotne pytanie okazało się nieważne i całkowicie nieinteresujące dla wszystkich uczestników. Jeśli masz wątpliwości, rzuć im wyzwanie i albo znikną, albo będziesz wiedział, co musisz zrobić. Uważam, że moim zdaniem dobrze jest mieć wątpliwości co do kodu / projektu. Inni nazywają to przeczuciem, ale aby rzucić wyzwanie tym problemom, musisz je najpierw rozpoznać. Odkąd zrobiłeś pierwszy krok, gratulacje! Teraz zejdź do króliczej nory ...
źródło
Posiadanie takiej klasy boga nigdy nie jest pożądane, ponieważ oznacza to nie tylko, że twoje pociski są teraz obiektami monolitycznymi, ale również to samo dotyczy algorytmu generowania procedur.
Pierwszym krokiem byłoby przeanalizowanie, dlaczego dokładnie twoja sztuczna inteligencja miała tyle problemów z radzeniem sobie ze złożonością twojego wzoru?
Czy przez przypadek próbowałeś przekształcić swoją sztuczną inteligencję w obiekt klasy boskiej, w pełni świadomy semantyki każdej możliwej właściwości? Jeśli tak, to właśnie stąd powstał problem.
Rozwiązaniem nie byłoby wówczas zintegrowanie wszystkich strategii z samą klasą pocisku, lecz odciążenie wskazówek dla sztucznej inteligencji z rdzenia AI do samych implementacji strategii.
To dałoby ci elastyczność, której początkowo pragnąłeś, w tym opcję rozszerzenia systemu o nowe strategie, jak chcesz, bez obawy, że napotkasz skutki uboczne związane ze starszymi zachowaniami.
Zamiast tego masz teraz wszystkie problemy związane z obiektami boskimi: nie tylko twoja klasa boska jest trudna do zrozumienia, trudny do debugowania monolitem, ale to samo dotyczy wszystkich innych komponentów mających dostęp do takiej boskiej klasy. Z powodu braku abstrakcji twoja sztuczna inteligencja musi teraz zmienić się w obrzydliwość o podobnej złożoności, ponieważ musi być teraz świadoma wszystkich zbędnych, indywidualnych właściwości.
Nawet teraz masz problemy z konserwacją. Problemy te nasilają się, zwłaszcza gdy stracisz członków zespołu, którzy nadal mają model poznawczy, jak ta klasa powinna działać.
Do tej pory każdy projekt, z którym się spotkałem, który korzystał z takich klas boga lub funkcji boga, albo był całkowicie przepisywany od zera, albo był przerywany, bez wyjątków.
źródło
Cały zły kod od zarania dziejów ma swoją historię, która sprawia, że krok po kroku wygląda rozsądnie. Twój nie jest wyjątkiem. Kodery uczą się przez kodowanie. Są aspekty twojego problemu, których nie mogłeś przewidzieć, które wydają się teraz oczywiste. Podejmowane przez ciebie decyzje były całkowicie uzasadnione przyrostowo, ale ogólnie poprowadziły Twoją architekturę w złym kierunku. Musisz zidentyfikować i ponownie przeanalizować te decyzje.
Zapytaj w biurze, czy ludzie mają jakieś pomysły, jak to naprawić. W większości miejsc, w których pracowałem, około 10-20% programistów ma prawdziwy talent do tego rodzaju rzeczy i właśnie czekało na szansę. Dowiedz się, kim są ci ludzie. Często twoi nowi pracownicy, którzy nie byli historycznie inwestowani w obecną architekturę, mogą najłatwiej dostrzec alternatywy. Połącz ich z jednym z weteranów, a możesz być zaskoczony tym, co wspólnie wymyślą.
źródło
W niektórych przypadkach jest to zdecydowanie dopuszczalne. Trudno mi jednak uwierzyć, że nie ma dobrego rozwiązania z wykorzystaniem zarówno generowania procedur, jak i twojej ładnej architektury zachowania opartej na załącznikach / komponentach. Jeśli wszystkie zachowania zostały właśnie wciągnięte do klasy pocisków, nie ma funkcjonalnej różnicy między boskim obiektem a zgrabną wersją architektoniczną. Co sprawiło, że korzystanie z algorytmów generowania procedur jest tak trudne?
Myślę, że nowe pytanie (może tutaj lub na gamedev.stackexchange.com?), W którym nakreśliłeś problemy, jakie miałeś z architekturą w połączeniu z proc. gen., byłoby naprawdę interesujące. Daj nam znać, jeśli również, jeśli zadajesz nowe pytanie!
źródło
Aby uzyskać inspirację, warto przyjrzeć się programowaniu funkcjonalnemu, a ściślej ściśle dopasowanym typom. Pomysł, że rzeczy nie działają, jest niemożliwy do przedstawienia w dostępnym systemie typów. Załóżmy na przykład, że masz broń z komponentami „strzelaj pociskami” i „trzymaj pociski”. Jeśli są to dwa oddzielne komponenty, istnieją konfiguracje, które nie mają sensu - możesz mieć broń, która strzela pociskami, ale nie ma ich w magazynie, lub broń, która przechowuje kule, ale ich nie strzela.
Na tym poziomie złożoności myślisz „dobrze, człowiek zorientuje się, że jest to bezużyteczne i uniknie niemożliwych kombinacji”. Lepszym sposobem może być uczynienie tego całkowicie niemożliwym. Na przykład komponent „strzelaj pociskami” może wymagać odniesienia do komponentu „trzymaj pociski” (lub po prostu trzymaj go jako część samego siebie, chociaż ma to swoje własne problemy).
Chociaż twoje przykłady mogą być znacznie bardziej skomplikowane, klucz wciąż ogranicza możliwe kombinacje, dodając ograniczenia. W pewnym sensie, jeśli jest to zbyt skomplikowane, aby generowanie procedur mogło się odbyć poprawnie, prawdopodobnie i tak jest zbyt skomplikowane. Pomyśl o wszystkich sposobach ograniczania się przy projektowaniu broni - czy mówisz sobie „tak, ta broń ma już miotacz ognia, nie ma sensu dodawać granatnika”? To coś, co możesz włączyć do swojej heurystyki. Możesz użyć mechanizmu prawdopodobieństwa, który jest nieco bardziej skomplikowany niż tylko zapewnienie stałej szansy posiadania każdego komponentu - możesz mieć komponenty, które mają tendencję do wykluczania się nawzajem lub komponenty, które zwykle działają dobrze razem.
Na koniec zastanów się, czy to rzeczywiście wystarczająco duży problem. Czy to niszczy zabawę z gry? Czy pistolety wynikowe są bezużyteczne lub przytłoczone? Ile? Czy jeden na 10 nonsensów? Gry takie jak Borderlands (z generowanymi proceduralnie efektami broni) często ignorują nonsensowne kombinacje efektów - jaki jest sens posiadania strzelby z lunetą 16x? A jednak zdarza się to bardzo często na Pograniczu. To tylko gra dla śmiechu, a nie jako awaria mechanizmów generowania :)
źródło