Jakie są sposoby na wyeliminowanie użycia przełącznika w kodzie?
design-patterns
Mariano
źródło
źródło
Odpowiedzi:
Instrukcje przełączające nie są same w sobie przeciwieństwem, ale jeśli kodujesz zorientowany obiektowo, powinieneś rozważyć, czy użycie przełącznika jest lepiej rozwiązane za pomocą polimorfizmu zamiast używania instrukcji switch.
W przypadku polimorfizmu to:
staje się tym:
źródło
typeof
, a ta odpowiedź nie sugeruje sposobów ani powodów do obejścia instrukcji switch w innych sytuacjach.Zobacz instrukcje Switch Zapach :
Zarówno refaktoryzacja, jak i refaktoryzacja do wzorców mają podejścia do rozwiązania tego problemu.
Jeśli twój (pseudo) kod wygląda tak:
Ten kod narusza zasadę Open Closed i jest wrażliwy na każdy nowy typ kodu akcji, który się pojawi. Aby temu zaradzić, możesz wprowadzić obiekt 'Command':
Jeśli twój (pseudo) kod wygląda tak:
Następnie możesz wprowadzić obiekt „Stan”.
Mam nadzieję że to pomoże.
źródło
Map<Integer, Command>
nie będzie wymagał przełącznika?Przełącznik to wzorzec, niezależnie od tego, czy został zaimplementowany za pomocą instrukcji switch, czy też łańcucha, tabeli przeglądowej, polimorfizmu oop, dopasowania wzorców lub czegoś innego.
Czy chcesz wyeliminować użycie „ instrukcji switch ” lub „ wzorca przełączania ”? ”? Pierwszą można wyeliminować, drugą tylko wtedy, gdy można zastosować inny wzorzec / algorytm, a najczęściej jest to niemożliwe lub nie jest to lepsze podejście.
Jeśli chcesz wyeliminować instrukcję switch z kodu, pierwsze pytanie, które należy zadać, dotyczy tego, gdzie ma sens wyeliminowanie instrukcji switch i użycie innej techniki. Niestety odpowiedź na to pytanie zależy od domeny.
I pamiętaj, że kompilatory mogą dokonywać różnych optymalizacji w celu przełączania instrukcji. Na przykład, jeśli chcesz wydajnie przetwarzać wiadomości, instrukcja switch jest prawie do zrobienia. Ale z drugiej strony prowadzenie reguł biznesowych opartych na instrukcji przełącznika prawdopodobnie nie jest najlepszym rozwiązaniem i aplikacja powinna zostać ponownie zarchiwizowana.
Oto kilka alternatyw dla zmiany instrukcji:
źródło
Przełączanie samo w sobie nie jest takie złe, ale jeśli masz dużo "przełączników" lub "if / else" na obiektach w swoich metodach, może to oznaczać, że twój projekt jest trochę "proceduralny" i że twoje obiekty są po prostu wartościowe wiadra. Przenieś logikę do swoich obiektów, wywołaj metodę na swoich obiektach i pozwól im zdecydować, jak zamiast tego odpowiedzieć.
źródło
Myślę, że najlepszym sposobem jest użycie dobrej mapy. Używając słownika, możesz odwzorować prawie każde wejście na inną wartość / obiekt / funkcję.
Twój kod wyglądałby mniej więcej tak (pseudo):
źródło
Każdy kocha OGROMNE
if else
bloki. Tak łatwe do odczytania! Jestem jednak ciekawy, dlaczego chciałbyś usunąć instrukcje switch. Jeśli potrzebujesz instrukcji switch, prawdopodobnie potrzebujesz instrukcji switch. Poważnie, powiedziałbym, że zależy to od tego, co robi kod. Jeśli wszystko, co robi przełącznik, to wywoływanie funkcji (powiedzmy), możesz przekazywać wskaźniki do funkcji. Czy to lepiej rozwiązanie, jest dyskusyjne.Myślę, że tutaj również język jest ważnym czynnikiem.
źródło
Myślę, że to, czego szukasz, to wzorzec strategii.
Można to zrealizować na kilka sposobów, o których wspominano w innych odpowiedziach na to pytanie, takich jak:
źródło
switch
instrukcje byłyby dobre do zastąpienia, jeśli zauważysz, że dodajesz nowe stany lub nowe zachowanie do instrukcji:Dodanie nowego zachowania wymaga skopiowania
switch
, a dodanie nowych stanów oznacza dodanie kolejnegocase
do każdejswitch
instrukcji.W Javie możesz przełączać tylko bardzo ograniczoną liczbę typów pierwotnych, których wartości znasz w czasie wykonywania. Stanowi to problem sam w sobie: stany są przedstawiane jako magiczne liczby lub znaki.
if - else
Można użyć dopasowania wzorców i wielu bloków, chociaż w rzeczywistości mają te same problemy podczas dodawania nowych zachowań i nowych stanów.Rozwiązanie, które inni sugerowali jako „polimorfizm”, jest przykładem wzorca State :
Zastąp każdy ze stanów własną klasą. Każde zachowanie ma swoją własną metodę w klasie:
Za każdym razem, gdy dodajesz nowy stan, musisz dodać nową implementację
IState
interfejsu. Naswitch
świecie dodawałbyś acase
do każdegoswitch
.Za każdym razem, gdy dodajesz nowe zachowanie, musisz dodać nową metodę do
IState
interfejsu i każdej implementacji. Jest to takie samo obciążenie jak poprzednio, chociaż teraz kompilator sprawdzi, czy masz implementacje nowego zachowania w każdym istniejącym stanie.Inni już powiedzieli, że to może być zbyt ciężkie, więc oczywiście jest punkt, w którym osiągasz, w którym przechodzisz od jednego do drugiego. Osobiście drugi raz, gdy piszę przełącznik, jest punktem, w którym dokonuję refaktoryzacji.
źródło
Jeśli inaczej
Obalam założenie, że przełącznik jest z natury zły.
źródło
Po pierwsze, nie wiedziałem, że używanie przełącznika jest anty-wzorcem.
Po drugie, przełącznik można zawsze zastąpić instrukcjami if / else if.
źródło
Dlaczego chcesz? W rękach dobrego kompilatora instrukcja switch może być znacznie bardziej wydajna niż bloki if / else (a także być łatwiejsza do odczytania), a tylko największe przełączniki mogą zostać przyspieszone, jeśli zostaną zastąpione jakimkolwiek rodzajem struktury danych wyszukiwania pośredniego.
źródło
„Przełącznik” to tylko konstrukcja językowa, a wszystkie konstrukcje językowe można traktować jako narzędzia do wykonania pracy. Podobnie jak w przypadku prawdziwych narzędzi, niektóre narzędzia lepiej nadają się do jednego zadania niż inne (nie używałbyś młotka kowalskiego do zawieszenia haczyka na zdjęcia). Ważną częścią jest to, jak definiuje się „wykonanie pracy”. Czy musi być łatwy w utrzymaniu, czy musi być szybki, czy musi być skalowany, czy musi być rozszerzalny i tak dalej.
W każdym punkcie procesu programowania istnieje zwykle szereg konstrukcji i wzorców, których można użyć: przełącznik, sekwencja if-else-if, funkcje wirtualne, tabele skoków, mapy ze wskaźnikami funkcji i tak dalej. Z doświadczeniem programista instynktownie będzie wiedział, jakiego narzędzia użyć w danej sytuacji.
Należy założyć, że każdy, kto konserwuje lub przegląda kod, jest co najmniej tak wykwalifikowany, jak oryginalny autor, tak aby każdy konstrukt mógł być bezpiecznie używany.
źródło
Jeśli przełącznik służy do rozróżniania różnych rodzajów obiektów, prawdopodobnie brakuje niektórych klas do precyzyjnego opisania tych obiektów lub niektórych metod wirtualnych ...
źródło
Dla C ++
Jeśli odnosisz się np. Do AbstractFactory, myślę, że metoda registerCreatorFunc (..) jest zwykle lepsza niż dodawanie przypadku dla każdego „nowego” stwierdzenia, które jest potrzebne. Następnie pozwolenie wszystkim klasom stworzyć i zarejestrować funkcję creatorFunction (..), którą można łatwo zaimplementować za pomocą makra (jeśli odważę się wspomnieć). Uważam, że jest to powszechne podejście, które stosuje wiele ram. Po raz pierwszy zobaczyłem to w ET ++ i myślę, że wiele frameworków, które wymagają makra DECL i IMPL, używa go.
źródło
W języku proceduralnym, takim jak C, zmiana będzie lepsza niż jakakolwiek inna alternatywa.
W języku zorientowanym obiektowo prawie zawsze dostępne są inne alternatywy, które lepiej wykorzystują strukturę obiektu, zwłaszcza polimorfizm.
Problem z instrukcjami switch pojawia się, gdy kilka bardzo podobnych bloków przełączników występuje w wielu miejscach aplikacji i należy dodać obsługę nowej wartości. Deweloper często zapomina o dodaniu obsługi nowej wartości do jednego z bloków przełączników rozrzuconych po aplikacji.
W przypadku polimorfizmu nowa klasa zastępuje nową wartość, a nowe zachowanie jest dodawane jako część dodawania nowej klasy. Zachowanie w tych punktach przełączania jest następnie dziedziczone z nadklasy, zastępowane w celu zapewnienia nowego zachowania lub implementowane w celu uniknięcia błędu kompilatora, gdy metoda super jest abstrakcyjna.
Tam, gdzie nie ma wyraźnego polimorfizmu, warto zastosować wzorzec Strategii .
Ale jeśli twoją alternatywą jest duży blok JEŻELI ... WTEDY ... INNY, to zapomnij o tym.
źródło
Użyj języka, który nie zawiera wbudowanej instrukcji przełącznika. Przychodzi mi na myśl Perl 5.
Ale poważnie, dlaczego miałbyś chcieć tego uniknąć? A jeśli masz dobry powód, aby tego uniknąć, dlaczego po prostu tego nie uniknąć?
źródło
Wskaźniki funkcji to jeden ze sposobów na zastąpienie ogromnej, obszernej instrukcji przełącznika, są szczególnie dobre w językach, w których można przechwytywać funkcje według ich nazw i tworzyć z nimi rzeczy.
Oczywiście nie powinieneś wymuszać instrukcji switch ze swojego kodu i zawsze istnieje szansa, że zrobisz to wszystko źle, co skutkuje głupimi, zbędnymi fragmentami kodu. (Czasami jest to nieuniknione, ale dobry język powinien pozwolić na usunięcie nadmiarowości przy zachowaniu czystości).
Oto wspaniały przykład dziel i rządź:
Powiedzmy, że masz jakiegoś tłumacza.
Zamiast tego możesz użyć tego:
Zauważ, że nie wiem, jak usunąć nadmiarowość opcode_table w C. Być może powinienem zadać pytanie. :)
źródło
Najbardziej oczywistą i niezależną od języka odpowiedzią jest użycie serii „jeśli”.
Jeśli język, którego używasz, ma wskaźniki funkcji (C) lub funkcje, które są wartościami pierwszej klasy (Lua), możesz osiągnąć wyniki podobne do "przełącznika" przy użyciu tablicy (lub listy) funkcji (wskaźników do).
Jeśli chcesz uzyskać lepsze odpowiedzi, powinieneś być bardziej szczegółowy w języku.
źródło
Instrukcje przełączania często można zastąpić dobrym projektem obiektu obiektowego.
Na przykład masz klasę Konto i używasz instrukcji przełącznika, aby wykonać inne obliczenia w zależności od typu konta.
Sugerowałbym, aby zastąpić to szeregiem klas kont reprezentujących różne typy kont i wszystkie z interfejsem konta.
Zmiana staje się wtedy zbędna, ponieważ wszystkie typy rachunków można traktować tak samo, a dzięki polimorfizmowi zostanie przeprowadzona odpowiednia kalkulacja dla typu rachunku.
źródło
Zależy, dlaczego chcesz go wymienić!
Wielu interpreterów używa „obliczonych gotos” zamiast instrukcji switch do wykonywania kodu operacji.
To, czego brakuje mi w przełączniku C / C ++, to Pascal „in” i zakresy. Chciałbym też móc włączyć struny. Ale te, choć trywialne dla kompilatora do jedzenia, są ciężką pracą, gdy są wykonywane przy użyciu struktur, iteratorów i innych rzeczy. Wręcz przeciwnie, jest wiele rzeczy, które chciałbym wymienić na przełącznik, gdyby tylko przełącznik C () był bardziej elastyczny!
źródło
Zmiana nie jest dobrym rozwiązaniem, ponieważ łamie zasadę Open Close. Tak to robię.
A oto jak z niego korzystać (biorąc swój kod):
Zasadniczo to, co robimy, to delegowanie odpowiedzialności na klasę dziecięcą, zamiast pozwalać rodzicowi decydować, co zrobić z dziećmi.
Możesz również przeczytać „Zasada zastępowania Liskova”.
źródło
W JavaScript z tablicą asocjacyjną:
to:
staje się tym:
Kurtuazja
źródło
Kolejny głos na if / else. Nie jestem wielkim fanem instrukcji case lub switch, ponieważ są ludzie, którzy ich nie używają. Kod jest mniej czytelny, jeśli używasz wielkości liter lub przełącznika. Może nie mniej czytelne dla Ciebie, ale dla tych, którzy nigdy nie musieli używać polecenia.
To samo dotyczy fabryk obiektów.
Bloki If / else to prosta konstrukcja, którą każdy dostaje. Jest kilka rzeczy, które możesz zrobić, aby upewnić się, że nie stwarzasz problemów.
Po pierwsze - nie próbuj wciskać instrukcji if więcej niż kilka razy. Jeśli zauważysz, że wciskasz się, robisz to źle.
Jest naprawdę źle - zrób to zamiast tego.
Do diabła z optymalizacją. Nie ma to większego znaczenia dla szybkości twojego kodu.
Po drugie, nie mam nic przeciwko wyrwaniu się z bloku If, o ile jest wystarczająco dużo instrukcji breaks rozproszonych w danym bloku kodu, aby było to oczywiste
EDYCJA : na Switchu i dlaczego myślę, że trudno to zrozumieć:
Oto przykład instrukcji switch ...
Dla mnie problemem jest to, że normalne struktury kontrolne, które mają zastosowanie w językach podobnych do C, zostały całkowicie zepsute. Istnieje ogólna zasada, że jeśli chcesz umieścić więcej niż jedną linię kodu wewnątrz struktury kontrolnej, użyj nawiasów klamrowych lub instrukcji begin / end.
na przykład
Dla mnie (i możesz mnie poprawić, jeśli się mylę), instrukcja Case wyrzuca tę regułę przez okno. Warunkowo wykonywany blok kodu nie jest umieszczany wewnątrz struktury początku / końca. Z tego powodu uważam, że Case jest koncepcyjnie na tyle inny, że nie można go używać.
Mam nadzieję, że to odpowiada na Twoje pytania.
źródło