Wie kann die Verwendung von Switch im Code vermieden werden?
design-patterns
Mariano
quelle
quelle
Antworten:
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:
wird dies:
quelle
typeof
, und diese Antwort schlägt keine Möglichkeiten oder Gründe vor, um switch-Anweisungen in anderen Situationen zu umgehen.Siehe die Switch-Anweisungen Geruch :
Sowohl Refactoring als auch Refactoring zu Mustern haben Ansätze, um dies zu beheben.
Wenn Ihr (Pseudo-) Code so aussieht:
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:
Wenn Ihr (Pseudo-) Code so aussieht:
Dann könnten Sie ein 'State'-Objekt einführen.
Hoffe das hilft.
quelle
Map<Integer, Command>
keinen Schalter benötigen?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:
quelle
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.
quelle
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):
quelle
Jeder liebt riesige
if else
Blö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.
quelle
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:
quelle
switch
Anweisungen sollten ersetzt werden, wenn Sie feststellen, dass Sie den Anweisungen neue Zustände oder neues Verhalten hinzufügen:Das Hinzufügen eines neuen Verhaltens erfordert das Kopieren des
switch
und das Hinzufügen neuer Zustände bedeutet das Hinzufügen eines weiterencase
zu jederswitch
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 - else
Blö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:
Jedes Mal, wenn Sie einen neuen Status hinzufügen, müssen Sie eine neue Implementierung der
IState
Schnittstelle hinzufügen . In einerswitch
Welt, dann würden Sie eine das Hinzufügencase
zu jederswitch
.Jedes Mal, wenn Sie ein neues Verhalten hinzufügen, müssen Sie der
IState
Schnittstelle 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.
quelle
ansonsten
Ich widerlege die Prämisse, dass der Wechsel von Natur aus schlecht ist.
quelle
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.
quelle
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.
quelle
'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.
quelle
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 ...
quelle
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.
quelle
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.
quelle
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?
quelle
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.
Stattdessen können Sie Folgendes verwenden:
Beachten Sie, dass ich nicht weiß, wie ich die Redundanz der opcode_table in C entfernen kann. Vielleicht sollte ich eine Frage dazu stellen. :) :)
quelle
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.
quelle
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.
quelle
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!
quelle
Ein Wechsel ist kein guter Weg, da er den Open Close Principal bricht. So mache ich es.
Und so verwenden Sie es (nehmen Sie Ihren Code):
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.
quelle
In JavaScript mit assoziativem Array:
dies:
wird dies:
Höflichkeit
quelle
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.
Ist wirklich schlecht - mach das stattdessen.
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
EDIT : On Switch und warum ich denke, es ist schwer zu groken:
Hier ist ein Beispiel für eine switch-Anweisung ...
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
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.
quelle