Ich versuche, den sauberen Codevorschlägen von Onkel Bob zu folgen und Methoden kurz zu halten.
Ich bin jedoch nicht in der Lage, diese Logik zu verkürzen:
if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}
Ich kann die anderen nicht entfernen und so das Ganze in kleinere Teile zerlegen, weil das "sonst" im "sonst wenn" die Leistung verbessert - die Bewertung dieser Bedingungen ist teuer, und wenn ich vermeiden kann, die folgenden Bedingungen zu bewerten, eine der ersten verursachen ist wahr, ich möchte sie vermeiden.
Auch semantisch gesehen macht es aus betriebswirtschaftlicher Sicht keinen Sinn, die nächste Bedingung zu bewerten, wenn die vorherige erfüllt wurde.
Bearbeiten: Diese Frage wurde als ein mögliches Duplikat von Elegant-Methoden für den Umgang mit if (if else) else identifiziert .
Ich glaube, das ist eine andere Frage (das sieht man auch, wenn man die Antworten dieser Fragen vergleicht).
- Meine Frage lautet, ob die erste akzeptierende Bedingung schnell endet .
- Die verknüpfte Frage versucht, alle Bedingungen zu akzeptieren , um etwas zu tun. (Besser in dieser Antwort auf diese Frage zu sehen: https://softwareengineering.stackexchange.com/a/122625/96955 )
quelle
Antworten:
Idealerweise sollten Sie Ihre Logik extrahieren, um den Warnungscode / die Warnungsnummer in eine eigene Methode umzuwandeln. So wird Ihr bestehender Code auf das Wesentliche reduziert
und GetConditionCode () kapselt die Logik zum Überprüfen von Bedingungen. Vielleicht ist es auch besser, eine Aufzählung als eine magische Zahl zu verwenden.
quelle
addAlert
nach einer falschen Alarmbedingung suchen mussAlertCode.None
.Das wichtige Maß ist die Komplexität des Codes, nicht die absolute Größe. Unter der Annahme, dass die verschiedenen Bedingungen nur einzelne Funktionsaufrufe sind und die Aktionen nicht komplexer sind als die von Ihnen gezeigten, würde ich sagen, dass am Code nichts falsch ist. Es ist schon so einfach wie es sein kann.
Jeder Versuch einer weiteren "Vereinfachung" wird die Dinge in der Tat komplizieren.
Natürlich können Sie das
else
Schlüsselwort durch ein ersetzen,return
wie andere vorgeschlagen haben, aber das ist nur eine Frage des Stils, nicht der Komplexität.Beiseite:
Mein allgemeiner Rat wäre, niemals religiös über eine Regel für sauberen Code zu werden: Die meisten Codierungsratschläge, die Sie im Internet sehen, sind gut, wenn sie in einem passenden Kontext angewendet werden, aber wenn Sie diese Ratschläge radikal überall anwenden, können Sie einen Einstieg gewinnen die IOCCC . Der Trick ist immer, ein Gleichgewicht zu finden, das es den Menschen ermöglicht, leicht über Ihren Code nachzudenken.
Verwenden Sie zu große Methoden, und Sie sind geschraubt. Verwenden Sie zu kleine Funktionen, und Sie sind geschraubt. Vermeiden Sie ternäre Ausdrücke, und Sie sind geschraubt. Verwenden Sie überall ternäre Ausdrücke, und Sie sind geschraubt. Stellen Sie fest, dass es Orte gibt, die einzeilige Funktionen erfordern, und Orte, die Funktionen mit 50 Zeilen erfordern (ja, sie existieren!). Stellen Sie fest, dass es Stellen gibt, an denen
if()
Anweisungen erforderlich sind , und Orte, an denen der?:
Operator erforderlich ist. Nutzen Sie das gesamte Arsenal, das Ihnen zur Verfügung steht, und versuchen Sie immer, das am besten geeignete Werkzeug zu verwenden, das Sie finden können. Und denken Sie daran, auch bei diesem Rat nicht religiös zu werden.quelle
else if
durch ein Inneresreturn
gefolgt von einem Einfachenif
(Entfernen deselse
) den Code schwieriger zu lesen machen könnte . Wenn der Code sagtelse if
, weiß ich sofort, dass der Code im nächsten Block nur ausgeführt wird, wenn der vorherige nicht ausgeführt wurde. Kein Muss, keine Aufregung. Wenn es eine Ebeneif
ist, wird sie möglicherweise ausgeführt oder nicht, unabhängig davon, ob die vorherige ausgeführt wurde. Jetzt muss ich einige mentale Anstrengungen unternehmen, um den vorherigen Block zu analysieren und festzustellen, dass er mit a endetreturn
. Ich würde diese mentale Anstrengung lieber darauf verwenden, die Geschäftslogik zu analysieren.else if
bildet es eine semantische Einheit. (Für den Compiler ist es nicht unbedingt eine Einheit, aber das ist in Ordnung.)...; return; } if (...
Tut es nicht. geschweige denn, wenn es auf mehrere Zeilen verteilt ist. Das ist etwas, worauf ich eigentlich achten muss, um zu sehen, was es tut, anstatt es direkt durch das bloße Anzeigen des Schlüsselwortpaars aufnehmen zu könnenelse if
.else if
Konstrukt vorziehen , zumal seine verkettete Form ein so bekanntes Muster ist. Code des Formularsif(...) return ...;
ist jedoch auch ein bekanntes Muster, daher würde ich das nicht vollständig verurteilen. Ich sehe dies jedoch als kleines Problem: Die Steuerungsablauflogik ist in beiden Fällen dieselbe, und ein genauerer Blick auf eineif(...) { ...; return; }
Leiter zeigt mir, dass dies tatsächlich einerelse if
Leiter entspricht. Ich sehe die Struktur eines einzelnen Begriffs, leite seine Bedeutung ab, merke, dass er sich überall wiederholt, und ich weiß, was los ist.else if
als auch verwendet werdenreturn
. zBelse if(...) { return alert();}
Es ist umstritten, ob dies für einen bestimmten Fall "besser" ist als die Ebene if..else. Aber wenn Sie wollen etwas anderes versuchen dies ist ein gemeinsamer Weg , es zu tun.
Fügen Sie Ihre Bedingungen in Objekte ein und fügen Sie diese Objekte in eine Liste ein
Wenn mehrere Aktionen erforderlich sind, können Sie eine verrückte Rekursion durchführen
Natürlich ja. Dies funktioniert nur, wenn Sie ein Muster für Ihre Logik haben. Wenn Sie versuchen, eine super-generische rekursive bedingte Aktion auszuführen, ist das Setup für das Objekt so kompliziert wie die ursprüngliche if-Anweisung. Sie erfinden Ihre eigene neue Sprache / Framework.
Aber Ihr Beispiel hat ein Muster
Ein häufiger Anwendungsfall für dieses Muster wäre die Validierung. Anstatt :
Wird
quelle
if...else
Leiter nur in die Konstruktion derConditions
Liste. Der Nettogewinn ist negativ, da die Konstruktion vonConditions
genauso viel Code wie der OP-Code benötigt, aber die zusätzliche Indirektion ist mit Kosten für die Lesbarkeit verbunden. Ich würde definitiv eine sauber codierte Leiter vorziehen.conditions
es aufgebaut ist ... ARG! Keine Annotation-Attribute! Warum Gott? Au meine Augen!Ziehen Sie in Betracht,
return;
nach dem Erfolg einer Bedingung alleelse
s zu sparen . Möglicherweise können Sie sogarreturn addAlert(1)
direkt, wenn diese Methode einen Rückgabewert hat.quelle
if
s nichts mehr passiert ... Das könnte eine vernünftige Annahme sein, und dann auch nicht.Ich habe Konstruktionen wie diese gesehen, die manchmal als sauberer angesehen werden:
Ternär mit richtigem Abstand kann auch eine gute Alternative sein:
Sie könnten auch versuchen, ein Array mit einem Paar zu erstellen, das Bedingung und Funktion enthält, und es durchlaufen, bis die erste Bedingung erfüllt ist - was, wie ich sehe, Ewans erster Antwort entspricht.
quelle
case
Labels verwendet werden?Als Variante von @ Ewans Antwort könnten Sie eine Kette (anstelle einer "flachen Liste") von Bedingungen wie folgt erstellen :
Auf diese Weise können Sie Ihre Bedingungen in einer definierten Reihenfolge anwenden, und die Infrastruktur (die dargestellte abstrakte Klasse) überspringt die verbleibenden Prüfungen, nachdem die erste erfüllt wurde.
Hier ist es dem Ansatz der "flachen Liste" überlegen, bei dem Sie das "Überspringen" in der Schleife implementieren müssen, die die Bedingungen anwendet.
Sie richten einfach die Konditionskette ein:
Und starten Sie die Auswertung mit einem einfachen Aufruf:
quelle
Erstens ist der ursprüngliche Code IMO nicht schrecklich. Es ist ziemlich verständlich und nichts ist von Natur aus schlecht.
Wenn Sie es nicht mögen, bauen Sie auf @ Ewans Idee auf, eine Liste zu verwenden, aber sein etwas unnatürliches
foreach break
Muster zu entfernen :Passen Sie dies nun in der Sprache Ihrer Wahl an, machen Sie jedes Element der Liste zu einem Objekt, einem Tupel, was auch immer, und Sie sind gut.
EDIT: Es sieht so aus, als wäre es nicht so klar, wie ich dachte. Lassen Sie mich das näher erläutern.
conditions
ist eine geordnete Liste irgendeiner Art;head
wird das aktuelle Element untersucht - am Anfang ist es das erste Element der Liste, und jedes Mal, wennnext()
es aufgerufen wird, wird es das folgende;check()
undalert()
sind dascheckConditionX()
undaddAlert(X)
vom OP.quelle
while not
anstelle vonforeach break
.Der Frage fehlen einige Einzelheiten. Wenn die Bedingungen sind:
oder wenn der inhalt
addAlert
komplizierter ist, dann wäre eine möglicherweise bessere lösung in say c #:Tupel sind in c # <8 nicht so schön, aber aus Gründen der Sicherheit ausgewählt.
Der Vorteil dieser Methode ist, dass die Struktur statisch typisiert ist, auch wenn keine der oben genannten Optionen zutrifft. Sie können nicht versehentlich vermasseln, indem Sie sagen, ein
else
.quelle
Der beste Weg, um die Komplexität der Zyklomatik in Fällen zu reduzieren, in denen Sie viel zu tun haben,
if->then statements
besteht darin, ein Wörterbuch oder eine Liste (sprachabhängig) zu verwenden, um den Schlüsselwert (if-Anweisungswert oder einen Wert von) und dann ein Wert- / Funktionsergebnis zu speichern.Zum Beispiel anstelle von (C #):
Ich kann einfach
Wenn Sie modernere Sprachen verwenden, können Sie mehr Logik als nur Werte speichern (c #). Dies sind eigentlich nur Inline-Funktionen, aber Sie können auch einfach auf andere Funktionen verweisen, wenn die Logik zu eng ist, um sie in die Linie zu setzen.
quelle
Ihr Code ist bereits zu kurz, aber die Logik selbst sollte nicht geändert werden. Auf den ersten Blick sieht es so aus, als würden Sie sich mit vier Anrufen wiederholen
checkCondition()
, und es ist nur offensichtlich, dass jeder nach erneutem sorgfältigen Lesen des Codes anders ist. Sie sollten korrekte Formatierungs- und Funktionsnamen hinzufügen, z.Ihr Code sollte vor allem lesbar sein. Ich glaube, nachdem er mehrere Bücher von Onkel Bob gelesen hat, ist dies die Botschaft, die er konsequent vermitteln will.
quelle
Angenommen, alle Funktionen sind in derselben Komponente implementiert, könnten Sie dafür sorgen, dass die Funktionen einen bestimmten Status beibehalten, um die mehreren Zweige im Ablauf zu entfernen.
EG:
checkCondition1()
würde werdenevaluateCondition1()
, auf dem es prüfen würde, ob vorherige Bedingung erfüllt wurden; In diesem Fall wird ein Wert zwischengespeichert, der von abgerufen werden sollgetConditionNumber()
.checkCondition2()
würde werdenevaluateCondition2()
, auf dem es prüfen würde, ob die bisherigen Bedingungen erfüllt waren. Wenn die vorherige Bedingung nicht erfüllt wurde, wird nach Bedingungsszenario 2 gesucht und ein Wert zwischengespeichert, von dem abgerufen werden sollgetConditionNumber()
. Und so weiter.BEARBEITEN:
Hier ist, wie die Prüfung auf teure Bedingungen implementiert werden müsste, damit dieser Ansatz funktioniert.
Wenn Sie zu viele teure Überprüfungen durchführen müssen und die in diesem Code enthaltenen Informationen vertraulich bleiben, können Sie diese auf diese Weise beibehalten und die Reihenfolge der Überprüfungen bei Bedarf ändern.
Diese Antwort bietet nur einen alternativen Vorschlag von den anderen Antworten und wird wahrscheinlich nicht besser als der ursprüngliche Code sein, wenn wir nur 4 Codezeilen berücksichtigen. Dies ist zwar kein schrecklicher Ansatz (und erschwert auch nicht die Wartung, wie andere bereits gesagt haben) in Anbetracht des erwähnten Szenarios (zu viele Überprüfungen, nur die Hauptfunktion wird als öffentlich angezeigt, alle Funktionen sind Implementierungsdetails derselben Klasse).
quelle
anyCondition() != false
.true
, möchte das OP nicht, dass die Bedingung 3 bewertet wird.anyCondition() != false
innerhalb von Funktionen nachsehen kannevaluateConditionXX()
. Dies ist realisierbar. Wenn der Ansatz der Verwendung des internen Status nicht gewünscht ist, verstehe ich, aber das Argument, dass dies nicht funktioniert, ist nicht gültig.Mehr als zwei "else" -Klauseln zwingen den Leser des Codes, die gesamte Kette zu durchlaufen, um den gewünschten zu finden. Verwenden Sie eine Methode wie: void AlertUponCondition (Bedingungsbedingung) {switch (Bedingung) {case Condition.Con1: ... break; case Condition.Con2: ... break; etc ...} Wobei "Bedingung" eine richtige Aufzählung ist. Geben Sie bei Bedarf einen Bool oder einen Wert zurück. Nennen Sie es wie folgt: AlertOnCondition (GetCondition ());
Einfacher geht es wirklich nicht und schneller als die If-else-Kette, wenn Sie einige Fälle überschreiten.
quelle
Ich kann nicht für Ihre spezielle Situation sprechen, da der Code nicht spezifisch ist, aber ...
Code wie dieser ist oft ein Geruch für ein fehlendes OO-Modell. Sie haben wirklich vier Arten von Dingen, von denen jede ihrem eigenen Alarmertyp zugeordnet ist. Anstatt jedoch diese Entitäten zu erkennen und für jede Instanz eine Klasse zu erstellen, behandeln Sie sie als eine Sache und versuchen, dies zu einem späteren Zeitpunkt nachzuholen Sie müssen wissen, womit Sie es zu tun haben, um fortzufahren.
Polymorphismus könnte Ihnen besser gefallen haben.
Seien Sie misstrauisch gegenüber Code mit langen Methoden, die lange oder komplexe If-Then-Konstrukte enthalten. Oft möchten Sie dort einen Klassenbaum mit einigen virtuellen Methoden.
quelle