Möglichkeiten zum Umschalten des Code-Schalters [geschlossen]


175

Wie kann die Verwendung von Switch im Code vermieden werden?


18
Warum sollten Sie bei sachgemäßer Verwendung Schalter entfernen? Bitte können Sie Ihre Frage näher erläutern.
RB.

Ich stimme dafür, dass diese Frage von der Community bearbeitet werden kann, weil alle das Gleiche sagen und es vielleicht schön ist, alle Meinungen zu konsolidieren;)
Josh

2
Der Schalter ist nicht so Standard wie andere Anweisungen. In C ++ werden Sie beispielsweise wahrscheinlich die Unterbrechung vergessen und dann unerwartete Ergebnisse erzielen. Außerdem ist diese "Pause" einer GOTO zu ähnlich. Ich habe nie versucht, Schalter zu eliminieren, aber sicher sind sie nicht meine Lieblingsanweisung. ;)

2
Ich denke, er bezieht sich auf das Switch-Konzept und nicht auf die C ++ - oder Java-Switch-Anweisung. Ein Schalter kann auch eine Kette von 'if-else if'-Blöcken sein.
Outlaw Programmer

2
Sie können amüsiert sein: Es gibt eine große Anzahl namhafter Programmierer aus der agilen Community, die sich der Anti-IF-Kampagne anschließen: antiifcampaign.com/supporters.html
Mike A

Antworten:


267

Switch-Anweisungen sind an sich kein Antimuster, aber wenn Sie objektorientiert codieren, sollten Sie überlegen, ob die Verwendung eines Switches besser mit Polymorphismus gelöst werden kann, anstatt eine switch-Anweisung zu verwenden.

Mit Polymorphismus ist dies:

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

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

wird dies:

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

2
Ich werde wegen eines ähnlichen Vorschlags in stackoverflow.com/questions/374239/ verprügelt. So viele Leute glauben nicht an Polymorphismus :) Sehr gutes Beispiel.
Nazgob

30
-1: Ich habe noch nie eine switch-Anweisung mit verwendet typeof, und diese Antwort schlägt keine Möglichkeiten oder Gründe vor, um switch-Anweisungen in anderen Situationen zu umgehen.
Kevin

3
Ich stimme @Kevin zu - ein Beispiel zeigt nicht wirklich, wie man Schalter durch Polymorphismus eliminiert. Die einfachen Beispiele: Ermitteln Sie den Namen der Aufzählung, indem Sie die Werte ändern, oder führen Sie einen Code mit geeigneten Werten in einem Algorithmus aus.
HotJard

Ich habe mich gefragt, wie ich es beseitigen kann, bevor Sie sich auf bestimmte Implementierungen verlassen können, z. B. in einer Fabrik. Und ich fand hier ein großartiges Beispiel stackoverflow.com/a/3434505/711855
juanmf

1
sehr triviales Beispiel.
GuardianX

239

Siehe die Switch-Anweisungen Geruch :

In der Regel sind ähnliche switch-Anweisungen über ein Programm verteilt. Wenn Sie eine Klausel in einem Switch hinzufügen oder entfernen, müssen Sie häufig auch die anderen suchen und reparieren.

Sowohl Refactoring als auch Refactoring zu Mustern haben Ansätze, um dies zu beheben.

Wenn Ihr (Pseudo-) Code so aussieht:

class RequestHandler {

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

Dieser Code verstößt gegen das Open Closed-Prinzip und ist für jede neue Art von Aktionscode anfällig. Um dies zu beheben, können Sie ein 'Command'-Objekt einführen:

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();
    }
}

Wenn Ihr (Pseudo-) Code so aussieht:

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");
        }
    }

Dann könnten Sie ein 'State'-Objekt einführen.

// 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();
    }
}

Hoffe das hilft.


7
Vielen Dank für das großartige Beispiel zum Refactor von Code. Obwohl ich am Anfang sagen könnte, dass es ein wenig schwer zu lesen ist (weil man zwischen mehreren Dateien wechseln muss, um es vollständig zu verstehen)
rshimoda

8
Die Argumente gegen switch sind gültig, solange Sie feststellen, dass die polymorphe Lösung die Einfachheit des Codes beeinträchtigt. Wenn Sie Ihre Switch-Fälle immer in Aufzählungen speichern, werden Sie von einigen Compilern darüber gewarnt, dass im Switch Status fehlen.
Harvey

1
Dies ist ein großartiges Beispiel für vollständige / unvollständige Operationen und natürlich die Umstrukturierung von Code in OOP. Danke vielmals. Ich denke, es wird nützlich sein, wenn Befürworter von OOP / Design Patterns vorschlagen, OOP-Konzepte eher wie Operatoren als wie Konzepte zu behandeln . Ich meine, dass "Erweitert", "Factory", "Implementiert" usw. so häufig für Dateien, Klassen und Zweige verwendet werden. Sie sollten einfach sein als Operatoren wie "+", "-", "+ =", "?:", "==", "->" usw. Wenn ein Programmierer sie in seinem Kopf so einfach wie Operatoren verwendet, nur dann kann er in der gesamten Klassenbibliothek über den Programmstatus und (in) vollständige Operationen nachdenken.
Namespaceform

13
Ich fange an zu denken, dass SWITCH im Vergleich dazu viel verständlicher und logischer ist. Und ich mag OOP normalerweise sehr, aber diese Entschließung scheint mir zu abstrakt
JK

1
Würde der zu generierende Code mit dem Command-Objekt Map<Integer, Command>keinen Schalter benötigen?
Ataulm

41

Ein Schalter ist ein Muster, unabhängig davon, ob es mit einer switch-Anweisung implementiert ist, ob es sich um eine Kette, eine Nachschlagetabelle, einen OOP-Polymorphismus, einen Mustervergleich oder etwas anderes handelt.

Möchten Sie die Verwendung der " switch-Anweisung " oder des " switch-Musters " vermeiden ? Das erste kann eliminiert werden, das zweite nur, wenn ein anderes Muster / ein anderer Algorithmus verwendet werden kann, und meistens ist dies nicht möglich oder es ist kein besserer Ansatz, dies zu tun.

Wenn Sie die switch-Anweisung aus dem Code entfernen möchten, müssen Sie zunächst die Frage stellen, wo es sinnvoll ist, die switch-Anweisung zu entfernen und eine andere Technik zu verwenden. Leider ist die Antwort auf diese Frage domänenspezifisch.

Und denken Sie daran, dass Compiler verschiedene Optimierungen vornehmen können, um Anweisungen zu wechseln. Wenn Sie beispielsweise die Nachrichtenverarbeitung effizient durchführen möchten, ist eine switch-Anweisung genau das Richtige. Andererseits ist das Ausführen von Geschäftsregeln auf der Grundlage einer switch-Anweisung wahrscheinlich nicht der beste Weg, und die Anwendung sollte erneut durchsucht werden.

Hier sind einige Alternativen zur switch-Anweisung:


1
Könnte jemand einen Vergleich der Nachrichtenverarbeitung mit Switch mit anderen Alternativen geben?
Mike A

37

Das Wechseln an sich ist nicht so schlimm, aber wenn Sie in Ihren Methoden viele "Schalter" oder "Wenn / Sonst" für Objekte haben, kann dies ein Zeichen dafür sein, dass Ihr Design ein bisschen "prozedural" ist und dass Ihre Objekte nur Wert sind Eimer. Verschieben Sie die Logik auf Ihre Objekte, rufen Sie eine Methode für Ihre Objekte auf und lassen Sie sie entscheiden, wie sie stattdessen reagieren sollen.


Vorausgesetzt natürlich, er schreibt nicht in C. :)
Bernard

1
in C kann er (ab?) Funktionszeiger und Strukturen verwenden, um etwas Objektähnliches zu erstellen;)
Tetha

Sie können FORT ^ H ^ H ^ H ^ H Java in jeder Sprache schreiben. ; p
Bernard

Völlig einverstanden - Schalter ist eine großartige Möglichkeit, Codezeilen zu reduzieren, aber spielen Sie nicht zu viel damit.
HotJard

21

Ich denke, der beste Weg ist, eine gute Karte zu verwenden. Mit einem Wörterbuch können Sie fast jede Eingabe einem anderen Wert / Objekt / einer anderen Funktion zuordnen.

Ihr Code würde ungefähr so ​​aussehen (Pseudo):

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

Object/Action DoStuff(Object key){
    return Map[key];
}

4
Kommt auf die Sprache an. Es kann viel weniger lesbar werden als ein Schalter
Vinko Vrsalovic

Dies ist eine schöne elegante Lösung in der richtigen Situation. Ich habe diese Mapping-Schlüsselcodes kürzlich erstellt und sie schienen für diesen Zweck in Ordnung zu sein.
Bernard

Dies ist wahr, ich würde dies wahrscheinlich nicht für etwas Einfaches verwenden, aber es bietet ein gewisses Maß an Flexibilität in Bezug auf die Konfiguration über eine switch-Anweisung. Ein Wörterbuch kann im laufenden Betrieb erstellt werden, während ein Schalter immer fest codiert ist.
Josh

Kann unter Umständen sauberer sein. Auch langsamer, da es Funktionsaufrufe erfordert.
Nick Johnson

1
Das hängt von Ihren Schlüsseln ab. Ein Compiler kann eine switch-Anweisung entweder in eine einfache Suche oder in eine extrem schnelle statische Binärsuche kompilieren, ohne dass Funktionsaufrufe erforderlich sind.
Nick Johnson

12

Jeder liebt riesige if elseBlöcke. So einfach zu lesen! Ich bin gespannt, warum Sie switch-Anweisungen entfernen möchten. Wenn Sie eine switch-Anweisung benötigen, benötigen Sie wahrscheinlich eine switch-Anweisung. Im Ernst, ich würde sagen, es hängt davon ab, was der Code tut. Wenn der Switch nur Funktionen aufruft (z. B.), können Sie Funktionszeiger übergeben. Ob es eine bessere Lösung ist, ist umstritten.

Sprache ist auch hier ein wichtiger Faktor, denke ich.



6

Ich denke, was Sie suchen, ist das Strategiemuster.

Dies kann auf verschiedene Arten implementiert werden, die in anderen Antworten auf diese Frage erwähnt wurden, wie zum Beispiel:

  • Eine Karte der Werte -> Funktionen
  • Polymorphismus. (Der Untertyp eines Objekts entscheidet, wie es einen bestimmten Prozess handhabt.)
  • Erstklassige Funktionen.

5

switch Anweisungen sollten ersetzt werden, wenn Sie feststellen, dass Sie den Anweisungen neue Zustände oder neues Verhalten hinzufügen:

int state;

String getString () {
   Schalter (Zustand) {
     Fall 0: // Verhalten für Zustand 0
           return "zero";
     Fall 1: // Verhalten für Zustand 1
           gib "eins" zurück;
   }}
   neue IllegalStateException () auslösen;
}}

double getDouble () {

   switch (this.state) {
     Fall 0: // Verhalten für Zustand 0
           return 0d;
     Fall 1: // Verhalten für Zustand 1
           return 1d;
   }}
   neue IllegalStateException () auslösen;
}}

Das Hinzufügen eines neuen Verhaltens erfordert das Kopieren des switchund das Hinzufügen neuer Zustände bedeutet das Hinzufügen eines weiteren casezu jeder switch Anweisung.

In Java können Sie nur eine sehr begrenzte Anzahl von primitiven Typen wechseln, deren Werte Sie zur Laufzeit kennen. Dies stellt an und für sich ein Problem dar: Zustände werden als magische Zahlen oder Zeichen dargestellt.

Mustervergleich und mehrere if - elseBlöcke können verwendet werden, haben jedoch dieselben Probleme beim Hinzufügen neuer Verhaltensweisen und neuer Zustände.

Die Lösung, die andere als "Polymorphismus" vorgeschlagen haben, ist ein Beispiel für das Zustandsmuster :

Ersetzen Sie jeden Status durch eine eigene Klasse. Jedes Verhalten hat eine eigene Methode für die Klasse:

IState Zustand;

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

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

Jedes Mal, wenn Sie einen neuen Status hinzufügen, müssen Sie eine neue Implementierung der IStateSchnittstelle hinzufügen . In einer switchWelt, dann würden Sie eine das Hinzufügen casezu jeder switch.

Jedes Mal, wenn Sie ein neues Verhalten hinzufügen, müssen Sie der IStateSchnittstelle und jeder Implementierung eine neue Methode hinzufügen . Dies ist die gleiche Belastung wie zuvor, obwohl der Compiler jetzt überprüft, ob Sie Implementierungen des neuen Verhaltens für jeden bereits vorhandenen Status haben.

Andere haben bereits gesagt, dass dies zu schwer sein könnte, also gibt es natürlich einen Punkt, an dem Sie von einem zum anderen wechseln. Persönlich ist das zweite Mal, dass ich einen Schalter schreibe, der Punkt, an dem ich umgestalte.


4

ansonsten

Ich widerlege die Prämisse, dass der Wechsel von Natur aus schlecht ist.


3

Zum einen wusste ich nicht, dass die Verwendung von Switch ein Anti-Pattern ist.

Zweitens kann switch immer durch if / else if-Anweisungen ersetzt werden.


genau - switch ist nur syntaktisches Methadon für eine Reihe von if / elsifs.
Mike A

3

Warum willst du? In den Händen eines guten Compilers kann eine switch-Anweisung weitaus effizienter sein als if / else-Blöcke (und auch leichter zu lesen), und nur die größten Switches werden wahrscheinlich beschleunigt, wenn sie durch irgendeine Art ersetzt werden der indirekten Suchdatenstruktur.


2
An diesem Punkt erraten Sie den Compiler und nehmen Änderungen an Ihrem Design basierend auf den internen Compiler-Funktionen vor. Das Design sollte der Art des Problems folgen, nicht der Art des Compilers.
Mike A

3

'switch' ist nur ein Sprachkonstrukt und alle Sprachkonstrukte können als Werkzeuge angesehen werden, um einen Job zu erledigen. Wie bei echten Werkzeugen eignen sich einige Werkzeuge besser für eine Aufgabe als für eine andere (Sie würden keinen Vorschlaghammer verwenden, um einen Bilderhaken anzubringen). Der wichtige Teil ist, wie definiert wird, wie man die Arbeit erledigt. Muss es wartbar sein, muss es schnell sein, muss es skaliert werden, muss es erweiterbar sein und so weiter.

An jedem Punkt des Programmierprozesses gibt es normalerweise eine Reihe von Konstrukten und Mustern, die verwendet werden können: einen Schalter, eine Wenn-Sonst-Wenn-Sequenz, virtuelle Funktionen, Sprungtabellen, Karten mit Funktionszeigern und so weiter. Mit der Erfahrung wird ein Programmierer instinktiv das richtige Werkzeug für eine bestimmte Situation kennen.

Es muss davon ausgegangen werden, dass jeder, der Code verwaltet oder überprüft, mindestens so erfahren ist wie der ursprüngliche Autor, damit jedes Konstrukt sicher verwendet werden kann.


OK, aber warum brauchen wir 5 verschiedene, redundante Methoden, um dasselbe zu tun - bedingte Ausführung?
Mike A

@ mike.amy: Weil jede Methode unterschiedliche Vorteile und Kosten hat und es darum geht, den größten Nutzen bei geringsten Kosten zu erzielen.
Skizz

1

Wenn der Schalter dazu dient, zwischen verschiedenen Arten von Objekten zu unterscheiden, fehlen Ihnen wahrscheinlich einige Klassen, um diese Objekte genau zu beschreiben, oder einige virtuelle Methoden ...


1

Für C ++

Wenn Sie sich auf eine AbstractFactory beziehen, ist eine registerCreatorFunc (..) -Methode normalerweise besser, als für jede "neue" Anweisung, die benötigt wird, einen Fall hinzuzufügen. Dann lassen Sie alle Klassen eine CreatorFunction (..) erstellen und registrieren, die einfach mit einem Makro implementiert werden kann (wenn ich es wage zu erwähnen). Ich glaube, dies ist ein gängiger Ansatz, den viele Frameworks verfolgen. Ich habe es zum ersten Mal in ET ++ gesehen und ich denke, dass viele Frameworks, die ein DECL- und IMPL-Makro erfordern, es verwenden.


1

In einer prozeduralen Sprache wie C ist der Wechsel dann besser als jede der Alternativen.

In einer objektorientierten Sprache gibt es fast immer andere Alternativen, die die Objektstruktur besser nutzen, insbesondere den Polymorphismus.

Das Problem mit switch-Anweisungen tritt auf, wenn mehrere sehr ähnliche switch-Blöcke an mehreren Stellen in der Anwendung auftreten und die Unterstützung für einen neuen Wert hinzugefügt werden muss. Es kommt häufig vor, dass Entwickler vergessen, einem der in der Anwendung verteilten Switch-Blöcke Unterstützung für den neuen Wert hinzuzufügen.

Beim Polymorphismus ersetzt eine neue Klasse den neuen Wert, und das neue Verhalten wird als Teil des Hinzufügens der neuen Klasse hinzugefügt. Das Verhalten an diesen Schaltpunkten wird dann entweder von der Oberklasse geerbt, überschrieben, um neues Verhalten bereitzustellen, oder implementiert, um einen Compilerfehler zu vermeiden, wenn die Supermethode abstrakt ist.

Wo es keinen offensichtlichen Polymorphismus gibt, kann es sich lohnen, das Strategiemuster umzusetzen .

Aber wenn Ihre Alternative ein großer WENN ... DANN ... SONST-Block ist, dann vergessen Sie ihn.


1

Verwenden Sie eine Sprache, die keine integrierte switch-Anweisung enthält. Perl 5 fällt mir ein.

Im Ernst, warum sollten Sie es vermeiden wollen? Und wenn Sie gute Gründe haben, dies zu vermeiden, warum vermeiden Sie es dann nicht einfach?


1

Funktionszeiger sind eine Möglichkeit, eine große, klobige switch-Anweisung zu ersetzen. Sie eignen sich besonders gut für Sprachen, in denen Sie Funktionen anhand ihrer Namen erfassen und mit ihnen erstellen können.

Natürlich sollten Sie switch-Anweisungen nicht aus Ihrem Code herauszwingen, und es besteht immer die Möglichkeit, dass Sie alles falsch machen, was zu dummen redundanten Codeteilen führt. (Dies ist manchmal unvermeidlich, aber eine gute Sprache sollte es Ihnen ermöglichen, Redundanz zu beseitigen, während Sie sauber bleiben.)

Dies ist ein großartiges Beispiel für das Teilen und Erobern:

Angenommen, Sie haben einen Dolmetscher.

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

Stattdessen können Sie Folgendes verwenden:

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

Beachten Sie, dass ich nicht weiß, wie ich die Redundanz der opcode_table in C entfernen kann. Vielleicht sollte ich eine Frage dazu stellen. :) :)


0

Die naheliegendste, sprachunabhängige Antwort ist die Verwendung einer Reihe von "Wenn".

Wenn die von Ihnen verwendete Sprache Funktionszeiger (C) oder Funktionen mit Werten der 1. Klasse (Lua) enthält, können Sie mit einem Array (oder einer Liste) von (Zeigern auf) Funktionen ähnliche Ergebnisse wie bei einem "Schalter" erzielen.

Sie sollten die Sprache genauer kennen, wenn Sie bessere Antworten wünschen.


0

Switch-Anweisungen können häufig durch ein gutes OO-Design ersetzt werden.

Sie haben beispielsweise eine Kontoklasse und verwenden eine switch-Anweisung, um eine andere Berechnung basierend auf dem Kontotyp durchzuführen.

Ich würde vorschlagen, dass dies durch eine Reihe von Kontoklassen ersetzt wird, die die verschiedenen Kontotypen darstellen und alle eine Kontofläche implementieren.

Der Wechsel wird dann unnötig, da Sie alle Arten von Konten gleich behandeln können und dank Polymorphismus die entsprechende Berechnung für den Kontotyp ausgeführt wird.


0

Kommt darauf an, warum du es ersetzen willst!

Viele Interpreter verwenden 'berechnete gotos' anstelle von switch-Anweisungen für die Opcode-Ausführung.

Was ich am C / C ++ - Switch vermisse, ist das Pascal-In und die Bereiche. Ich wünschte auch, ich könnte Saiten einschalten. Obwohl dies für einen Compiler trivial ist, ist es harte Arbeit, wenn Strukturen, Iteratoren und Dinge verwendet werden. Im Gegenteil, ich wünschte, ich könnte viele Dinge durch einen Schalter ersetzen, wenn nur Cs switch () flexibler wäre!


0

Ein Wechsel ist kein guter Weg, da er den Open Close Principal bricht. So mache ich es.

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");
   }
}

Und so verwenden Sie es (nehmen Sie Ihren Code):

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

Grundsätzlich delegieren wir die Verantwortung an die Kinderklasse, anstatt die Eltern entscheiden zu lassen, was mit Kindern geschehen soll.

Vielleicht möchten Sie auch das "Liskov-Substitutionsprinzip" nachlesen.


0

In JavaScript mit assoziativem Array:
dies:

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;
    }
}

wird dies:

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);
}

Höflichkeit


-12

Noch eine Abstimmung für if / else. Ich bin kein großer Fan von case- oder switch-Anweisungen, weil es einige Leute gibt, die sie nicht verwenden. Der Code ist weniger lesbar, wenn Sie case oder switch verwenden. Vielleicht nicht weniger lesbar für Sie, aber für diejenigen, die den Befehl noch nie gebraucht haben.

Gleiches gilt für Objektfabriken.

If / else-Blöcke sind ein einfaches Konstrukt, das jeder bekommt. Sie können einige Dinge tun, um sicherzustellen, dass Sie keine Probleme verursachen.

Erstens: Versuchen Sie nicht, Anweisungen mehr als ein paar Mal einzurücken. Wenn Sie feststellen, dass Sie einrücken, dann machen Sie es falsch.

 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

Ist wirklich schlecht - mach das stattdessen.

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

Optimierung sei verdammt. Es macht keinen großen Unterschied für die Geschwindigkeit Ihres Codes.

Zweitens bin ich nicht abgeneigt, aus einem If-Block auszubrechen, solange genügend Unterbrechungsanweisungen über den jeweiligen Codeblock verteilt sind, um dies offensichtlich zu machen

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

EDIT : On Switch und warum ich denke, es ist schwer zu groken:

Hier ist ein Beispiel für eine switch-Anweisung ...

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));
 }

Für mich ist das Problem hier, dass die normalen Kontrollstrukturen, die in C-ähnlichen Sprachen gelten, vollständig zerstört wurden. Es gibt eine allgemeine Regel, dass Sie, wenn Sie mehr als eine Codezeile in eine Kontrollstruktur einfügen möchten, geschweifte Klammern oder eine begin / end-Anweisung verwenden.

z.B

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

Für mich (und Sie können mich korrigieren, wenn ich falsch liege) wirft die Case-Anweisung diese Regel aus dem Fenster. Ein bedingt ausgeführter Codeblock wird nicht in eine Anfangs- / Endstruktur eingefügt. Aus diesem Grund glaube ich, dass Case konzeptionell so unterschiedlich ist, dass es nicht verwendet werden kann.

Hoffe das beantwortet deine Fragen.


Wow - offensichtlich eine umstrittene Antwort. Es würde mich interessieren, was ich so falsch gemacht habe.
Seanyboy

Äh, als zu komplex wechseln? Ich weiß nicht ... scheint mir, dass es nicht mehr viele Sprachfunktionen gibt, die Sie verwenden könnten. :) Wäre es in Ihrem zentralen Beispiel nicht klüger, etwas zu tun, wenn (a == 1 || a == 2 || a == 3) etwas tut?
Lars Westergren

In Ihrem letzten Beispiel führt "break" in den meisten Sprachen nichts aus, was aus dem nächsten Block (normalerweise einer Schleife) herausbricht, was in Ihrem Fall ohnehin in der nächsten Zeile (endif) geschieht. Wenn Sie eine Sprache verwenden, in der break "return" ist, werden viele Rückgaben ebenfalls verpönt (mit Ausnahme von "Guard Statements")
Lars Westergren,

4
"Optimierung ist verdammt. Es macht keinen großen Unterschied für die Geschwindigkeit Ihres Codes." Mein Code läuft auf einer mobilen Plattform. Optimierungen sind wichtig. Außerdem können Schalter bei korrekter Verwendung dazu führen, dass der Code SEHR sauber aussieht (im Vergleich zu if..elseif..elseif..elseif ...). Noch nie gesehen? Lerne sie.
Swati

Das Wechseln ist überhaupt nicht kompliziert, aber ich habe immer versucht, die Anzahl der im Code verwendeten Konstrukte zu minimieren, um die Reibung beim Verständnis zu verringern.
Seanyboy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.