Sposoby na wyeliminowanie zmiany w kodzie [zamknięte]

175

Jakie są sposoby na wyeliminowanie użycia przełącznika w kodzie?

Mariano
źródło
18
Dlaczego miałbyś chcieć wyeliminować przełączniki, jeśli są odpowiednio używane? Proszę, czy możesz rozwinąć swoje pytanie.
RB.
Głosuję na to pytanie, aby można było je edytować przez społeczność, ponieważ wszyscy mówią to samo i byłoby miło skonsolidować wszystkie opinie;)
Josh
2
Przełącznik nie jest tak standardowy, jak inne instrukcje. Na przykład w C ++ prawdopodobnie zapomnisz „przerwy”, a wtedy otrzymasz nieoczekiwane rezultaty. Poza tym ta „przerwa” jest zbyt podobna do GOTO. Nigdy nie próbowałem eliminować przełączników, ale na pewno nie są to moje ulubione instrukcje. ;)
2
Myślę, że odnosi się do koncepcji przełącznika, a nie instrukcji przełącznika C ++ lub Java. Przełącznik może być również łańcuchem bloków „if-else if”.
Outlaw Programmer
2
Możesz być rozbawiony: jest wielu znanych programistów ze społeczności Agile, którzy dołączyli do kampanii przeciwko IF: antiifcampaign.com/supporters.html
Mike A

Odpowiedzi:

267

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:

foreach (var animal in zoo) {
    switch (typeof(animal)) {
        case "dog":
            echo animal.bark();
            break;

        case "cat":
            echo animal.meow();
            break;
    }
}

staje się tym:

foreach (var animal in zoo) {
    echo animal.speak();
}
mlarsen
źródło
2
Zostałem uderzony z powodu podobnej sugestii w stackoverflow.com/questions/374239/ ... Tak wielu ludzi nie wierzy w polimorfizm :) Bardzo dobry przykład.
Nazgob
30
-1: Nigdy nie używałem instrukcji switch z typeof, a ta odpowiedź nie sugeruje sposobów ani powodów do obejścia instrukcji switch w innych sytuacjach.
Kevin
3
Zgadzam się z @Kevinem - podany przykład tak naprawdę nie pokazuje, jak wyeliminować przełącznik przez polimorfizm. Proste przykłady: pobierz nazwę wyliczenia, zmieniając jego wartości, lub wykonaj kod za pomocą odpowiednich wartości w jakimś algorytmie.
HotJard
Zastanawiałem się, jak to wyeliminować, zanim będzie można polegać na konkretnych implementacjach, czyli w fabryce. I znalazłem tutaj świetny przykład stackoverflow.com/a/3434505/711855
juanmf
1
bardzo trywialny przykład.
GuardianX
239

Zobacz instrukcje Switch Zapach :

Zazwyczaj podobne instrukcje przełączające są rozproszone w całym programie. Jeśli dodasz lub usuniesz klauzulę w jednym przełączniku, często musisz znaleźć i naprawić inne.

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:

class RequestHandler {

    public void handleRequest(int action) {
        switch(action) {
            case LOGIN:
                doLogin();
                break;
            case LOGOUT:
                doLogout();
                break;
            case QUERY:
               doQuery();
               break;
        }
    }
}

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':

interface Command {
    public void execute();
}

class LoginCommand implements Command {
    public void execute() {
        // do what doLogin() used to do
    }
}

class RequestHandler {
    private Map<Integer, Command> commandMap; // injected in, or obtained from a factory
    public void handleRequest(int action) {
        Command command = commandMap.get(action);
        command.execute();
    }
}

Jeśli twój (pseudo) kod wygląda tak:

class House {
    private int state;

    public void enter() {
        switch (state) {
            case INSIDE:
                throw new Exception("Cannot enter. Already inside");
            case OUTSIDE:
                 state = INSIDE;
                 ...
                 break;
         }
    }
    public void exit() {
        switch (state) {
            case INSIDE:
                state = OUTSIDE;
                ...
                break;
            case OUTSIDE:
                throw new Exception("Cannot leave. Already outside");
        }
    }

Następnie możesz wprowadzić obiekt „Stan”.

// Throw exceptions unless the behavior is overriden by subclasses
abstract class HouseState {
    public HouseState enter() {
        throw new Exception("Cannot enter");
    }
    public HouseState leave() {
        throw new Exception("Cannot leave");
    }
}

class Inside extends HouseState {
    public HouseState leave() {
        return new Outside();
    }
}

class Outside extends HouseState {
    public HouseState enter() {
        return new Inside();
    }
}

class House {
    private HouseState state;
    public void enter() {
        this.state = this.state.enter();
    }
    public void leave() {
        this.state = this.state.leave();
    }
}

Mam nadzieję że to pomoże.

zestaw narzędzi
źródło
7
Dziękuję za świetny przykład refaktoryzacji kodu. Chociaż na początku mogę powiedzieć, że jest trochę trudny do odczytania (ponieważ trzeba przełączać się między kilkoma plikami, aby w pełni to zrozumieć)
rshimoda
8
Argumenty przeciwko przełącznikowi są ważne, o ile zdasz sobie sprawę, że rozwiązanie polimorficzne wiąże się z utratą prostoty kodu. Ponadto, jeśli zawsze przechowujesz przypadki przełączników w wyliczeniach, niektóre kompilatory będą ostrzegać, że w przełączniku brakuje stanów.
Harvey
1
To świetny przykład kompletnych / niekompletnych operacji i oczywiście przebudowy kodu na OOP. Dzięki za tonę. Myślę, że przydatne będzie, jeśli zwolennicy OOP / Design Patterns sugerują traktowanie koncepcji OOP jako operatorów, a nie pojęć . Chodzi mi o to, że „rozszerzenia”, „fabryka”, „narzędzia” itp. Są używane tak często w plikach, klasach, gałęziach. Powinny być proste, jak operatory takie jak „+”, „-”, „+ =”, „?:”, „==”, „->” itd. Gdy programista używa ich w swoim umyśle jako operatorów, tylko wtedy może przemyśleć całą bibliotekę klas o stanie programu i (nie) zakończonych operacjach.
namespaceform
13
Zaczynam myśleć, że SWITCH jest dużo bardziej zrozumiały i logiczny w porównaniu z tym. I zwykle bardzo lubię OOP, ale ta rezolucja wydaje mi się zbyt abstrakcyjna
JK
1
Czy w przypadku obiektu Command kod do wygenerowania Map<Integer, Command>nie będzie wymagał przełącznika?
ataulm
41

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:

Pop Catalin
źródło
1
Czy ktoś mógłby porównać przetwarzanie wiadomości za pomocą przełącznika z innymi alternatywami?
Mike A
37

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ć.

Lars Westergren
źródło
Zakładając oczywiście, że nie pisze w C. :)
Bernard
1
w C może (ab?) użyć wskaźników funkcji i struktur do zbudowania czegoś podobnego do obiektu;)
Tetha
Możesz napisać FORT ^ H ^ H ^ H ^ H Java w dowolnym języku. ; p
Bernard,
Całkowicie się zgadzam - przełącznik to świetny sposób na redukcję linii kodu, ale nie baw się nim też mach.
HotJard
21

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):

void InitMap(){
    Map[key1] = Object/Action;
    Map[key2] = Object/Action;
}

Object/Action DoStuff(Object key){
    return Map[key];
}
Josh
źródło
4
Zależy od języka. Może być o wiele mniej czytelny niż przełącznik
Vinko Vrsalovic
To ładne, eleganckie rozwiązanie w odpowiedniej sytuacji. Ostatnio zrobiłem to mapowanie kodów klawiszy i wydawało się, że w tym celu czyta się dobrze.
Bernard,
To prawda, prawdopodobnie nie użyłbym tego do niczego prostego, ale zapewnia pewną elastyczność w zakresie konfiguracji w instrukcji przełącznika. Słownik można przygotować w locie, podczas gdy przełącznik zawsze będzie zakodowany na stałe.
Josh
W pewnych okolicznościach może być czystszy. Również wolniej, ponieważ wymaga wywołań funkcji.
Nick Johnson,
1
To zależy od twoich kluczy. Kompilator może skompilować instrukcję switch w postaci prostego wyszukiwania lub niezwykle szybkiego statycznego wyszukiwania binarnego, w każdym przypadku bez konieczności wywoływania funkcji.
Nick Johnson,
12

Każdy kocha OGROMNE if elsebloki. 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.

Bernarda
źródło
13
Zakładam, że to był sarkazm :)
Craig Day,
6

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:

  • Mapa wartości -> funkcje
  • Wielopostaciowość. (podtyp obiektu zadecyduje o sposobie obsługi określonego procesu).
  • Funkcje pierwszej klasy.
AJ.
źródło
5

switch instrukcje byłyby dobre do zastąpienia, jeśli zauważysz, że dodajesz nowe stany lub nowe zachowanie do instrukcji:

stan int;

String getString () {
   switch (state) {
     case 0: // zachowanie dla stanu 0
           return "zero";
     przypadek 1: // zachowanie dla stanu 1
           return "jeden";
   }
   wyrzucić nowy IllegalStateException ();
}

double getDouble () {

   switch (this.state) {
     case 0: // zachowanie dla stanu 0
           return 0d;
     przypadek 1: // zachowanie dla stanu 1
           return 1d;
   }
   wyrzucić nowy IllegalStateException ();
}

Dodanie nowego zachowania wymaga skopiowania switch, a dodanie nowych stanów oznacza dodanie kolejnego casedo każdej switch 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 - elseMoż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:

Stan państwa;

String getString () {
   return state.getString ();
}

double getDouble () {
   return state.getDouble ();
}

Za każdym razem, gdy dodajesz nowy stan, musisz dodać nową implementację IStateinterfejsu. Na switchświecie dodawałbyś a casedo każdego switch.

Za każdym razem, gdy dodajesz nowe zachowanie, musisz dodać nową metodę do IStateinterfejsu 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.

Jamesh
źródło
4

Jeśli inaczej

Obalam założenie, że przełącznik jest z natury zły.

Iain Holder
źródło
3

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.

SCdF
źródło
dokładnie - przełącznik to po prostu syntaktyczny metadon dla zestawu if / elsif.
Mike A
3

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.

Nick Johnson
źródło
2
W którym momencie zastanawiasz się nad kompilatorem i wprowadzasz zmiany w projekcie w oparciu o wewnętrzne elementy kompilatora. Projekt powinien odpowiadać naturze problemu, a nie naturze kompilatora.
Mike A
3

„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.

Skizz
źródło
OK, ale dlaczego potrzebujemy 5 różnych, zbędnych sposobów zrobienia tego samego - warunkowego wykonania?
Mike A
@ mike.amy: Ponieważ każda metoda ma inne korzyści i koszty, a chodzi o uzyskanie jak największych korzyści przy jak najmniejszych kosztach.
Skizz
1

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 ...

Xavier Nodet
źródło
1

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.

epatel
źródło
1

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.

Bill Michell
źródło
1

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ąć?

innaM
źródło
1

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.

switch(*IP) {
    case OPCODE_ADD:
        ...
        break;
    case OPCODE_NOT_ZERO:
        ...
        break;
    case OPCODE_JUMP:
        ...
        break;
    default:
        fixme(*IP);
}

Zamiast tego możesz użyć tego:

opcode_table[*IP](*IP, vm);

... // in somewhere else:
void opcode_add(byte_opcode op, Vm* vm) { ... };
void opcode_not_zero(byte_opcode op, Vm* vm) { ... };
void opcode_jump(byte_opcode op, Vm* vm) { ... };
void opcode_default(byte_opcode op, Vm* vm) { /* fixme */ };

OpcodeFuncPtr opcode_table[256] = {
    ...
    opcode_add,
    opcode_not_zero,
    opcode_jump,
    opcode_default,
    opcode_default,
    ... // etc.
};

Zauważ, że nie wiem, jak usunąć nadmiarowość opcode_table w C. Być może powinienem zadać pytanie. :)

Wesoły
źródło
0

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.

Remo.D
źródło
0

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.

Jason
źródło
0

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!

Będzie
źródło
0

Zmiana nie jest dobrym rozwiązaniem, ponieważ łamie zasadę Open Close. Tak to robię.

public class Animal
{
       public abstract void Speak();
}


public class Dog : Animal
{
   public virtual void Speak()
   {
       Console.WriteLine("Hao Hao");
   }
}

public class Cat : Animal
{
   public virtual void Speak()
   {
       Console.WriteLine("Meauuuu");
   }
}

A oto jak z niego korzystać (biorąc swój kod):

foreach (var animal in zoo) 
{
    echo animal.speak();
}

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”.

Sheraz
źródło
0

W JavaScript z tablicą asocjacyjną:
to:

function getItemPricing(customer, item) {
    switch (customer.type) {
        // VIPs are awesome. Give them 50% off.
        case 'VIP':
            return item.price * item.quantity * 0.50;

            // Preferred customers are no VIPs, but they still get 25% off.
        case 'Preferred':
            return item.price * item.quantity * 0.75;

            // No discount for other customers.
        case 'Regular':
        case
        default:
            return item.price * item.quantity;
    }
}

staje się tym:

function getItemPricing(customer, item) {
var pricing = {
    'VIP': function(item) {
        return item.price * item.quantity * 0.50;
    },
    'Preferred': function(item) {
        if (item.price <= 100.0)
            return item.price * item.quantity * 0.75;

        // Else
        return item.price * item.quantity;
    },
    'Regular': function(item) {
        return item.price * item.quantity;
    }
};

    if (pricing[customer.type])
        return pricing[customer.type](item);
    else
        return pricing.Regular(item);
}

Kurtuazja

Premraj
źródło
-12

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.

 if a = 1 then 
     do something else 
     if a = 2 then 
         do something else
     else 
         if a = 3 then 
             do the last thing
         endif
     endif 
  endif

Jest naprawdę źle - zrób to zamiast tego.

if a = 1 then 
   do something
endif 
if a = 2 then 
   do something else
endif 
if a = 3 then 
   do something more
endif 

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

procedure processA(a:int)
    if a = 1 then 
       do something
       procedure_return
    endif 
    if a = 2 then 
       do something else
       procedure_return
    endif 
    if a = 3 then 
       do something more
       procedure_return
    endif 
end_procedure

EDYCJA : na Switchu i dlaczego myślę, że trudno to zrozumieć:

Oto przykład instrukcji switch ...

private void doLog(LogLevel logLevel, String msg) {
   String prefix;
   switch (logLevel) {
     case INFO:
       prefix = "INFO";
       break;
     case WARN:
       prefix = "WARN";
       break;
     case ERROR:
       prefix = "ERROR";
       break;
     default:
       throw new RuntimeException("Oops, forgot to add stuff on new enum constant");
   }
   System.out.println(String.format("%s: %s", prefix, msg));
 }

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

for i from 1 to 1000 {statement1; statement2}
if something=false then {statement1; statement2}
while isOKtoLoop {statement1; statement2}

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.

seanyboy
źródło
Wow - oczywiście kontrowersyjna odpowiedź. Chciałbym wiedzieć, co tak źle zrobiłem.
seanyboy
Eee, zmiana jako zbyt złożona? Nie wiem ... wydaje mi się, że nie zostało wiele funkcji językowych, których można by użyć. :) Poza tym, w twoim głównym przykładzie, czy nie byłoby mądrzejszy, gdyby (a == 1 || a == 2 || a == 3) coś zrobił?
Lars Westergren,
Ponadto w Twoim ostatnim przykładzie „break” nic nie da w większości języków - to wyrwie się z najbliższego bloku (zwykle pętli), co w twoim przypadku i tak dzieje się w następnej linii (endif). Jeśli używasz języka, w którym przerwa to „powrót”, wiele zwrotów jest również mile widzianych (z wyjątkiem „instrukcji strażnika”)
Lars Westergren,
4
„Do diabła z optymalizacją. Nie ma to większego znaczenia dla szybkości twojego kodu”. Mój kod działa na platformie mobilnej. Optymalizacje mają znaczenie. Co więcej, przełączniki mogą sprawić, że kod będzie wyglądał BARDZO czysto (w porównaniu do if..elseif..elseif..elseif ...), jeśli są używane poprawnie. Nigdy ich nie widziałem? Naucz się ich.
Swati
Zmiana nie jest wcale skomplikowana, ale zawsze starałem się minimalizować liczbę konstrukcji używanych w kodzie, aby zmniejszyć tarcia w zrozumieniu.
seanyboy,