Dostałem trochę kodu Java do obejrzenia, który symuluje wyścig samochodowy, w tym implementację podstawowej maszyny stanu. To nie jest klasyczna machina stanów komputerowych, lecz jedynie obiekt, który może mieć wiele stanów i może przełączać się między tymi stanami na podstawie serii obliczeń.
Aby opisać tylko problem, mam klasę samochodów z zagnieżdżoną klasą enum, która definiuje niektóre stałe dla stanu samochodu (takie jak OFF, IDLE, DRIVE, REVERSE itp.). W tej samej klasie samochodów mam funkcję aktualizacji, która zasadniczo składa się z dużej instrukcji przełączania, która włącza bieżący stan samochodu, wykonuje pewne obliczenia, a następnie zmienia stan samochodu.
Z tego, co widzę, stan Samochody jest używany tylko w swojej klasie.
Moje pytanie brzmi: czy jest to najlepszy sposób radzenia sobie z implementacją maszyny stanu o charakterze opisanym powyżej? Brzmi jak najbardziej oczywiste rozwiązanie, ale w przeszłości zawsze słyszałem, że „instrukcje przełączników są złe”.
Główny problem, jaki widzę tutaj, polega na tym, że instrukcja switch może stać się bardzo duża, ponieważ dodamy więcej stanów (jeśli zostanie to uznane za konieczne), a kod może stać się niewygodny i trudny do utrzymania.
Jakie byłoby lepsze rozwiązanie tego problemu?
źródło
object.state = object.function(object.state);
Odpowiedzi:
Za pomocą State Pattern zmieniłem samochód w swego rodzaju maszynę stanową . Zauważ, że nie
switch
lubif-then-else
instrukcje służą do wyboru stanu.W takich przypadkach wszystkie stany są klasami wewnętrznymi, ale można je zaimplementować w inny sposób.
Każdy stan zawiera prawidłowe stany, w których może się zmienić.
Użytkownik jest monitowany o następny stan w przypadku, gdy więcej niż jeden jest możliwy, lub po prostu o potwierdzenie, jeśli tylko jeden jest możliwy.
Możesz go skompilować i uruchomić, aby go przetestować.
Użyłem graficznego okna dialogowego, ponieważ łatwiej było interaktywnie uruchamiać go w Eclipse.
Schemat UML jest pobierany stąd .
źródło
To takie uproszczenie, które nadaje programowaniu obiektowemu złą nazwę. Używanie
if
jest tak samo „złe” jak używanie instrukcji switch. Tak czy inaczej, nie wysyłamy polimorficznie.Jeśli potrzebujesz reguły pasującej do zgryzu, wypróbuj tę:
Instrukcja switch, która nie jest zduplikowana nigdzie indziej w bazie kodu, może czasem udać, że nie jest zła. Jeśli sprawy nie są publiczne, ale są zamknięte, to naprawdę nie jest to sprawa nikogo innego. Zwłaszcza jeśli wiesz, jak i kiedy podzielić to na klasy. To, że możesz, nie oznacza, że musisz. To dlatego, że możesz, że mniej krytyczne jest zrobić to teraz.
Jeśli próbujesz wepchnąć coraz więcej rzeczy do instrukcji zamiany, rozpowszechniać wiedzę na temat spraw lub żałować, że nie było tak źle, aby po prostu zrobić kopię, to nadszedł czas, aby przekształcić sprawy w osobne klasy.
Jeśli masz czas, aby przeczytać więcej niż kilka dźwięków na temat refaktoryzacji instrukcji switch, c2 ma bardzo dobrze zrównoważoną stronę o zapachu instrukcji switch .
Nawet w kodzie OOP nie każdy przełącznik jest zły. Tak to wykorzystujesz i dlaczego.
źródło
Samochód jest rodzajem maszyny stanowej. Instrukcje switch są najprostszym sposobem na implementację maszyny stanów bez super stanów i pod stanów.
źródło
Instrukcje Switch nie są złe. Nie słuchaj ludzi, którzy mówią takie rzeczy, jak „przełączanie statystyk jest złe”! Niektóre szczególne zastosowania instrukcji switch są anty-wzorcowe, jak użycie switcha do emulacji podklasy. (Ale możesz również wdrożyć ten antypattern z ifs, więc chyba też są złe!).
Twoja implementacja brzmi dobrze. Masz rację, będzie trudna do utrzymania, jeśli dodasz wiele więcej stanów. Ale to nie jest tylko kwestia implementacji - posiadanie obiektu z wieloma stanami o różnych zachowaniach jest problemem. Obrazowanie twojego samochodu ma 25 stanów, z których każdy wykazywał inne zachowanie i różne reguły dotyczące zmian stanu. Samo określenie i udokumentowanie tego zachowania byłoby ogromnym zadaniem. Będziesz miał tysiące zasad dotyczących zmiany stanu! Rozmiar
switch
byłby tylko objawem większego problemu. Jeśli to możliwe, unikaj schodzenia tą drogą.Możliwym rozwiązaniem jest rozbicie stanu na niezależne podstacje. Na przykład, czy ODWRÓCENIE naprawdę różni się od DRIVE? Być może stany samochodu można podzielić na dwie części: stan silnika (WYŁ., Jałowy, NAPĘD) i kierunek (DO PRZODU, DO TYŁU). Stan i kierunek silnika będą prawdopodobnie w większości niezależne, więc ograniczysz powielanie logiki i reguły zmiany stanu. Więcej obiektów z mniejszą liczbą stanów jest łatwiejszych w zarządzaniu niż pojedynczy obiekt z wieloma stanami.
źródło
W twoim przykładzie samochody to po prostu maszyny stanowe w klasycznym sensie informatycznym. Mają mały, dobrze zdefiniowany zestaw stanów i pewną logikę przejścia stanu.
Moją pierwszą sugestią jest rozważenie podziału logiki przejścia na własną funkcję (lub klasę, jeśli twój język nie obsługuje funkcji pierwszej klasy).
Moją drugą sugestią jest rozważenie przełamania logiki przejścia do samego stanu, który miałby swoją własną funkcję (lub klasę, jeśli twój język nie obsługuje funkcji pierwszej klasy).
W obu schematach proces przejścia do stanu wyglądałby mniej więcej tak:
lub
Drugi może oczywiście być trywialnie zawinięty w klasie samochodów, aby wyglądał jak pierwszy.
W obu scenariuszach dodanie nowego stanu (powiedzmy DRAFTING) wymagałoby tylko dodania nowego typu obiektu stanu i zmiany obiektów, które konkretnie przełączają się na nowy stan.
źródło
To zależy od tego, jak duży
switch
może być.W twoim przykładzie myślę, że a
switch
jest w porządku, ponieważ tak naprawdę nie ma żadnego innego stanu, który mógłbym wymyślić, abyśCar
mógł mieć, więc z czasem nie zwiększyłby się.Jeśli jedynym problemem jest posiadanie dużego przełącznika, w którym każdy
case
ma wiele instrukcji, to po prostu określ odrębne prywatne metody dla każdego.Czasami ludzie sugerują wzorzec projektowania stanu , ale jest bardziej odpowiedni, gdy masz do czynienia ze złożoną logiką i stanami podejmującymi różne decyzje biznesowe dla wielu różnych operacji. W przeciwnym razie proste problemy powinny mieć proste rozwiązania.
W niektórych scenariuszach możesz mieć metody, które wykonują zadania tylko wtedy, gdy stan to A lub B, ale nie C lub D, lub mieć wiele metod z bardzo prostymi operacjami zależnymi od stanu. Wtedy jedno lub kilka
switch
stwierdzeń byłoby lepszych.źródło
To brzmi jak oldschoolowy automat państwowy, który był używany, zanim ktokolwiek zaczął programować obiektowo, nie mówiąc już o projektowaniu wzorów. Może być zaimplementowany w dowolnym języku, który ma instrukcje przełączników, takim jak C.
Jak powiedzieli inni, nie ma nic złego w instrukcjach zamiany. Alternatywy są często bardziej skomplikowane i trudniejsze do zrozumienia.
O ile liczba przełączników nie stanie się absurdalnie duża, sprawa może pozostać całkiem do opanowania. Pierwszym krokiem do zachowania jego czytelności jest zastąpienie kodu za każdym razem wywołaniem funkcji w celu zaimplementowania zachowania stanu.
źródło