Aus Gründen der Lesbarkeit definiere ich beim Aufrufen von Funktionen häufig temporäre Variablen, z. B. den folgenden Code
var preventUndo = true;
doSomething(preventUndo);
Die kürzere Version davon dazu wäre,
doSomething(true);
Aber wenn ich zum Code zurückkomme, frage ich mich oft, worauf true
sich das bezieht. Gibt es eine Konvention für diese Art von Rätsel?
coding-style
Duopixel
quelle
quelle
doSomething( Undo.PREVENT )
Undo = { PREVENT = true, DONT_PREVENT = false }
. Aber in JavaScript ist die Konvention, das so zu machen:function myFunction( mandatoryArg1, mandatoryArg2, otherArgs ) { /*...*/ }
und dannmyFunction( 1, 2, { option1: true, option2: false } )
.doSomething(preventUndo=True)
Antworten:
Variablen erklären
Ihr Fall ist ein Beispiel für die Einführung in das Variable Refactoring. Kurz gesagt, eine erklärende Variable ist eine Variable, die nicht unbedingt erforderlich ist, die es Ihnen jedoch ermöglicht, einer Sache einen eindeutigen Namen zu geben, um die Lesbarkeit zu verbessern.
Ein guter Qualitätscode teilt dem Leser seine Absicht mit. und als professioneller Entwickler sind Lesbarkeit und Wartbarkeit Ihre obersten Ziele.
Als Faustregel würde ich Folgendes empfehlen: Wenn der Zweck Ihres Parameters nicht sofort offensichtlich ist, können Sie eine Variable verwenden, um ihm einen guten Namen zu geben. Ich denke, dies ist eine gute Praxis im Allgemeinen (sofern nicht missbraucht). Hier ist ein kurzes Beispiel:
gegenüber dem etwas längeren, aber wohl klareren:
Boolesche Parameter
Ihr Beispiel zeigt tatsächlich, warum Boolesche Werte in APIs oft eine schlechte Idee sind - auf der aufrufenden Seite tun sie nichts, um zu erklären, was passiert. Erwägen:
Sie müssten nachsehen, was diese Parameter bedeuten. Wenn sie Aufzählungen wären, wäre es viel klarer:
Bearbeiten:
Es wurden Überschriften hinzugefügt und die Reihenfolge der beiden Hauptabsätze vertauscht, da sich zu viele Leute auf den Teil mit den booleschen Parametern konzentrierten (um fair zu sein, es war ursprünglich der erste Absatz). Fügte auch ein Beispiel zum ersten Teil hinzu.
quelle
doSomething(preventUndo: true);
ist dies hinreichend klar. Erstellen Sie auch eine Aufzählung für jeden Booleschen Wert in Ihrer API? Ernsthaft?Schreiben Sie keinen Code, den Sie nicht brauchen.
Wenn Sie
doSomething(true)
Schwierigkeiten haben zu verstehen, sollten Sie entweder einen Kommentar hinzufügen:oder, falls die Sprache dies unterstützt, den Parameternamen hinzufügen:
Andernfalls verlassen Sie sich auf Ihre IDE, um die Signatur der aufgerufenen Methode zu erhalten:
Fälle, in denen es nützlich ist
Das Einfügen einer zusätzlichen Variablen kann nützlich sein:
Zum Debuggen:
Der gleiche Code kann in eine einzelne Zeile geschrieben werden. Wenn Sie jedoch einen Haltepunkt setzen möchten, bevor Sie ein Produkt in den Warenkorb legen, um zu überprüfen, ob die Produkt-ID korrekt ist, empfiehlt es sich, zwei statt einer Zeile zu schreiben.
Wenn Sie eine Methode mit vielen Parametern haben und jeder Parameter aus einer Bewertung stammt. In einer Zeile kann dies völlig unlesbar werden.
Wenn die Auswertung eines Parameters zu kompliziert ist. Ein Beispiel für einen Code, den ich gesehen habe:
Warum nicht in anderen Fällen?
Warum sollten Sie in einfachen Fällen keine zusätzlichen Variablen erstellen?
Nicht wegen der Leistungseinbußen. Dies wäre eine sehr falsche Annahme eines Anfängers, der seine App mikrooptimiert . In den meisten Sprachen treten keine Leistungseinbußen auf, da der Compiler die Variable einfügt. In den Sprachen, in denen der Compiler dies nicht tut, können Sie einige Mikrosekunden gewinnen, indem Sie es von Hand einfügen, was es nicht wert ist. Tu das nicht.
Aber wegen der Gefahr, den Namen zu dissoziieren, geben Sie der Variablen den Namen des Parameters.
Beispiel:
Angenommen, der ursprüngliche Code lautet:
Später stellte ein Entwickler, der an
doSomething
Hinweisen arbeitet , dass der Verlauf des Rückgängigmachens kürzlich zwei neue Methoden eingeführt hat:Nun
preventUndo
scheint nicht sehr klar. Bedeutet das, dass nur die letzte Aktion verhindert wird? Oder bedeutet dies, dass der Benutzer die Funktion zum Rückgängigmachen nicht mehr verwenden kann? Oder dass der Rückgängig-Verlauf gelöscht wird? Die klareren Namen wären:Nun haben Sie:
Was ist mit Aufzählungen?
Einige andere Leute schlugen vor, Aufzählungen zu verwenden. Während es das unmittelbare Problem löst, schafft es ein größeres. Lass uns mal sehen:
Probleme damit:
Es ist zu viel Code.
if (preventUndo == undoPrevention.prevent)
? Ernsthaft?! Ich möchte nichtif
jedes Mal solche s schreiben .Das Hinzufügen eines Elements zur Aufzählung ist später sehr verlockend, wenn ich dieselbe Aufzählung an einer anderen Stelle verwende. Was ist, wenn ich es so ändere:
Was wird jetzt passieren? Funktioniert die
doSomething
Methode wie erwartet? Um dies zu verhindern, müsste diese Methode von Anfang an so geschrieben werden:Die Variante, die Boolesche Werte verwendet, sieht so gut aus!
quelle
preventUndo
zuallowUndo
in der Signatur ...Sie sollten auch keine Methoden mit Booleschen Flags schreiben.
Um Robert C. Martin zu zitieren, heißt es in seinem Buch Clean Code (ISBN-13 978-0-13-235088-4): "Das Übergeben eines Booleschen in eine Funktion ist eine wirklich schreckliche Praxis."
Um ihn zu paraphrasieren: Die Tatsache, dass Sie einen True / False-Schalter haben, bedeutet, dass Ihre Methode höchstwahrscheinlich zwei verschiedene Dinge ausführt (dh "Tun Sie etwas mit Rückgängig" und "Tun Sie etwas ohne Rückgängig") und sollte es daher sein Aufteilung in zwei verschiedene Methoden (die möglicherweise intern dasselbe aufrufen).
quelle
doSomethingUndoable()
könnte die Änderungen erfassen und dann "doSomething()
Wenn Sie in zwei Klassen zu sehen , wie gekoppelter sie sind, eine der Kategorien ist Datenkopplung , die in einer Klasse Aufrufe von Methoden einer anderen Klasse Code bezieht und nur in Daten übergibt, wie in welchem Monat Sie für einen Bericht mögen, und eine andere Kategorie ist die Steuerungskopplung , das Aufrufen von Methoden und die Übergabe von Elementen, die das Verhalten der Methode steuern. Das Beispiel, das ich in der Klasse verwende, ist eine
verbose
Flagge oder einereportType
Flagge, aber espreventUndo
ist auch ein großartiges Beispiel. Wie Sie gerade gezeigt haben, macht es die Steuerungskopplung schwierig, aufrufenden Code zu lesen und zu verstehen, was passiert. Außerdem wird der aufrufende Code für Änderungen anfällig,doSomething()
die dasselbe Flag verwenden, um das Rückgängigmachen und Archivieren zu steuern oder einen weiteren Parameter hinzuzufügendoSomething()
um die Archivierung zu kontrollieren und so Ihren Code zu beschädigen und so weiter.Das Problem ist, dass der Code zu eng gekoppelt ist. Das Übergeben eines Bools zur Verhaltenskontrolle ist meiner Meinung nach ein Zeichen für eine schlechte API. Wenn Sie die API besitzen, ändern Sie sie. Zwei Methoden
doSomething()
unddoSomethingWithUndo()
wären besser. Wenn Sie es nicht besitzen, schreiben Sie zwei Wrapper-Methoden selbst in den Code, den Sie besitzen, und lassen Sie einen von ihnendoSomething(true)
und den anderen aufrufendoSomething(false)
.quelle
Es ist nicht die Rolle des Aufrufers, die Rolle von Argumenten zu definieren. Ich greife immer zur Inline-Version und überprüfe im Zweifelsfall die Signatur der aufgerufenen Funktion.
Variable sollte nach ihrer Rolle im aktuellen Bereich benannt werden. Nicht Der Bereich, in den sie gesendet werden.
quelle
Was ich in JavaScript tun würde, ist, dass die Funktion ein Objekt als einzigen Parameter annimmt:
Dann nenne es mit:
quelle
doSomething(x)
Methode gemacht! lol ... Ich habe Ihren Beitrag bis jetzt nicht einmal bemerkt.Ich nutze gern Sprachfunktionen, um mir mehr Klarheit zu verschaffen. In C # können Sie beispielsweise Parameter nach Namen angeben:
In JavaScript bevorzuge ich normalerweise ein options-Objekt als Argument aus diesem Grund:
Das ist aber nur meine Präferenz. Ich nehme an, es hängt stark von der aufgerufenen Methode ab und davon, wie die IDE dem Entwickler hilft, die Signatur zu verstehen.
quelle
$.ajax({url:'', data:''})
etc etc.Ich finde, das ist am einfachsten und am einfachsten zu lesen:
MainMa gefällt das nicht. Möglicherweise müssen wir zustimmen, dass wir nicht zustimmen, oder wir können eine Lösung verwenden, die Joshua Bloch vorschlägt:
Wenn Sie jetzt jemals ein Undo.LIMITED_UNDO hinzufügen, wird Ihr Code erst kompiliert, wenn Sie die createUndoBuffer () -Methode implementieren. Und in doSomething () gibt es kein if (Undo.ALLOW == u). Ich habe es in beide Richtungen gemacht, und die zweite Methode ist ziemlich schwerfällig und ein wenig schwer zu verstehen, wenn die Undo-Aufzählung auf Seiten und Seiten Code erweitert wird, aber das lässt Sie nachdenken. Normalerweise halte ich mich an die einfachere Methode, um einen einfachen Booleschen Wert durch eine 2-Wert-Aufzählung zu ersetzen, bis ich Grund zur Änderung habe. Wenn ich einen dritten Wert hinzufüge, verwende ich meine IDE, um "Verwendungen zu finden" und dann alles zu reparieren.
quelle
Ich denke, Ihre Lösung macht Ihren Code ein bisschen lesbarer, aber ich würde es vermeiden, eine zusätzliche Variable zu definieren, nur um Ihren Funktionsaufruf klarer zu machen. Wenn Sie eine zusätzliche Variable verwenden möchten, würde ich diese als konstant markieren, wenn Ihre Programmiersprache dies unterstützt.
Ich kann mir zwei Alternativen vorstellen, die keine zusätzliche Variable beinhalten:
1. Verwenden Sie einen zusätzlichen Kommentar
2. Verwenden Sie eine zweiwertige Aufzählung anstelle eines Booleschen Werts
Sie können Alternative 2 verwenden, wenn Ihre Sprache Aufzählungen unterstützt.
BEARBEITEN
Die Verwendung benannter Argumente ist natürlich auch eine Option, wenn Ihre Sprache diese unterstützt.
In Bezug auf die zusätzliche Codierung, die von Enums benötigt wird, scheint es mir wirklich vernachlässigbar. Anstatt von
du hast
oder
Und selbst wenn Sie etwas mehr tippen, denken Sie daran, dass Code einmal geschrieben und mehrmals gelesen wird. Es kann sich also lohnen, jetzt etwas mehr zu tippen, um sechs Monate später einen besser lesbaren Code zu finden.
quelle
Ich stimme dem zu, was @GlenPeterson gesagt hat:
aber auch interessant wäre folgendes, da es für mich nur zwei möglichkeiten gibt (wahr oder falsch)
quelle
doSomething
, dem Aufrufer jedoch die Wahl lässt, ob das Rückgängigmachen zugelassen werden soll. Abhilfe kann eine Überladung schaffen, die die Option Boolean verwendet, aber auch Wrapper-Versionen, die die frühere Version mit dem Parameter always true oder always false aufrufen.Da Sie hauptsächlich nach Javascript fragen, können Sie mit coffeescript ein benanntes Argument wie die Syntax haben, super einfach:
kompiliert nach
Viel Spaß bei http://js2coffee.org/ , um die Möglichkeiten auszuprobieren
quelle
Nur für eine weniger konventionelle Lösung und da das OP Javascript erwähnte, besteht die Möglichkeit, ein assoziatives Array zum Abbilden von Werten zu verwenden. Der Vorteil gegenüber einem einzelnen Booleschen Wert ist, dass Sie so viele Argumente haben können, wie Sie möchten, und sich nicht um deren Reihenfolge sorgen müssen.
Mir ist klar, dass dies ein Reflex von jemandem mit stark typisiertem Hintergrund ist und möglicherweise nicht so praktisch ist, aber ich halte dies für originell.
Eine andere Möglichkeit ist, ein Objekt zu haben und ist auch in Javascript möglich. Ich bin nicht zu 100% sicher, dass dies syntaktisch korrekt ist. Schauen Sie sich Muster wie Factory an, um weitere Inspirationen zu erhalten.
quelle
doSomethingOptions.PREVENTUNDO
) anstelle der Array-Zugriffsnotation geschrieben werden.Der Fokus Ihrer Frage und der anderen Antworten liegt auf der Verbesserung der Lesbarkeit, wenn die Funktion aufgerufen wird. Diesem Fokus stimme ich zu. Alle spezifischen Richtlinien, Ereignis "keine booleschen Argumente", sollten immer als Mittel zu diesem Zweck fungieren und nicht zu einem Selbstzweck werden.
Ich denke, es ist nützlich zu bemerken, dass dieses Problem meistens vermieden wird, wenn die Programmiersprache benannte Argumente / Schlüsselwortargumente unterstützt, wie in C # 4 und Python, oder wenn die Methodenargumente im Methodennamen verschachtelt sind, wie in Smalltalk oder Objective-C.
Beispiele:
quelle
Man sollte vermeiden, boolesche Variablen als Argumente an Funktionen zu übergeben. Weil Funktionen eine Sache nach der anderen tun sollten. Durch die Übergabe einer booleschen Variablen hat die Funktion zwei Verhaltensweisen. Dies führt auch zu Lesbarkeitsproblemen für einen späteren Zeitpunkt oder für andere Programmierer, die dazu neigen, Ihren Code zu sehen. Dies führt auch zu Problemen beim Testen der Funktion. Wahrscheinlich müssen Sie in diesem Fall zwei Testfälle erstellen. Stellen Sie sich vor, Sie haben eine Funktion mit switch-Anweisung und ändern ihr Verhalten basierend auf dem Schaltertyp. Dann müssen Sie so viele verschiedene Testfälle definieren.
Immer wenn man auf ein boolesches Argument für die Funktion stößt, muss der Code so angepasst werden, dass überhaupt kein boolesches Argument geschrieben wird.
quelle
if(param) {...} else {...}
)?Ein guter Compiler für Low-Lavel-Sprachen wie C ++ optimiert den Maschinencode von mehr als 2 Zeilen wie folgt:
Es spielt also keine Rolle für die Leistung, erhöht aber die Lesbarkeit.
Es ist auch hilfreich, eine Variable zu verwenden, falls sie später variabel wird und möglicherweise auch andere Werte akzeptiert.
quelle