Kommentare
- Es scheint, dass MainMa und ich eine andere Definition des Flags " haben. " Ich dachte an Präprozessor #ifdefs. Nach welchem haben Sie gefragt?
- Dies ist eine wirklich gute Frage. Ich ' habe mich selbst sehr gefragt und tatsächlich festgestellt, dass ich " sage. 0d4f322cfc „>
etwas zu viel.
newItem = true
, dann einige Zeilen unter if (newItem ) then
Antwort
Das Problem, das ich bei der Verwaltung von Code gesehen habe, der Flags verwendet, ist, dass die Anzahl der Zustände schnell wächst und es fast immer nicht behandelte Zustände gibt. Ein Beispiel aus eigener Erfahrung: Ich habe an einem Code mit diesen drei Flags gearbeitet.
bool capturing, processing, sending;
Diese drei haben acht Zustände erstellt (tatsächlich gab es zwei andere Flags) auch). Nicht alle möglichen Wertekombinationen wurden vom Code abgedeckt, und Benutzer sahen Fehler:
if(capturing && sending){ // we must be processing as well ... }
Es stellte sich heraus, dass es Situationen gab, in denen die Annahme in der if-Anweisung erfolgte oben war falsch.
Flags neigen dazu, sich im Laufe der Zeit zu verstärken, und sie verbergen den tatsächlichen Zustand einer Klasse. Deshalb sollten sie vermieden werden.
Kommentare
- +1, " sollten vermieden werden ". Ich würde etwas über ' hinzufügen, aber in einigen Situationen sind Flags erforderlich ' (einige sagen möglicherweise ' ein notwendiges Übel ')
- @TrevorBoydSmith Nach meiner Erfahrung benötigen Sie nur ein wenig mehr als die durchschnittliche Gehirnleistung, für die Sie verwenden würden eine Flagge
- In Ihrem Beispiel sollte es sich um eine einzelne Aufzählung handeln, die den Status darstellt, nicht um 3 Boolesche Werte.
- Möglicherweise stoßen Sie auf ein ähnliches Problem wie bei I Ich stehe gerade vor mir. Zusätzlich zur Abdeckung aller möglichen Zustände können zwei Anwendungen dasselbe Flag verwenden (z. B. das Hochladen von Kundendaten). In diesem Fall wird nur ein Uploader das Flag verwenden, es auslösen und viel Glück beim Finden des Problems in der Zukunft.
Antwort
Hier ist ein Beispiel, wenn Flags nützlich sind.
Ich habe einen Code, der Passwörter generiert (unter Verwendung eines kryptografisch sicheren Pseudozufallszahlengenerators). Der Aufrufer der Methode wählt aus, ob oder Nicht das Passwort sollte Großbuchstaben, Kleinbuchstaben, Ziffern, Basissymbole, erweiterte Symbole, griechische Symbole, kyrillische Symbole und Unicode enthalten.
Mit Flags ist das Aufrufen dieser Methode einfach:
var password = this.PasswordGenerator.Generate( CharacterSet.Digits | CharacterSet.LowercaseLetters | CharacterSet.UppercaseLetters);
und es kann sogar vereinfacht werden zu:
var password = this.PasswordGenerator.Generate(CharacterSet.LettersAndDigits);
Was wäre die Methodensignatur ohne Flags?
public byte[] Generate( bool uppercaseLetters, bool lowercaseLetters, bool digits, bool basicSymbols, bool extendedSymbols, bool greekLetters, bool cyrillicLetters, bool unicode);
wird folgendermaßen aufgerufen:
// Very readable, isn"t it? // Tell me just by looking at this code what symbols do I want to be included? var password = this.PasswordGenerator.Generate( true, true, true, false, false, false, false, false);
Wie in den Kommentaren erwähnt, besteht ein anderer Ansatz darin, eine Sammlung zu verwenden:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LowercaseLetters, CharacterSet.UppercaseLetters, });
Dies ist viel besser lesbar als die Menge von true
und false
, hat aber immer noch zwei Nachteile:
Der Hauptnachteil besteht darin, kombinierte Werte wie CharacterSet.LettersAndDigits
Sie würden so etwas in der Generate()
-Methode schreiben:
if (set.Contains(CharacterSet.LowercaseLetters) || set.Contains(CharacterSet.Letters) || set.Contains(CharacterSet.LettersAndDigits) || set.Contains(CharacterSet.Default) || set.Contains(CharacterSet.All)) { // The password should contain lowercase letters. }
möglicherweise wie folgt umgeschrieben:
var lowercaseGroups = new [] { CharacterSet.LowercaseLetters, CharacterSet.Letters, CharacterSet.LettersAndDigits, CharacterSet.Default, CharacterSet.All, }; if (lowercaseGroups.Any(s => set.Contains(s))) { // The password should contain lowercase letters. }
Vergleichen Sie dies mit dem, was Sie haben, indem Sie Flags verwenden:
if (set & CharacterSet.LowercaseLetters == CharacterSet.LowercaseLetters) { // The password should contain lowercase letters. }
Die s Der zweite, sehr kleine Nachteil ist, dass nicht klar ist, wie sich die Methode verhalten würde, wenn sie wie folgt aufgerufen würde:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LettersAndDigits, // So digits are requested two times. });
Kommentare
- Ich glaube, OP bezieht sich auf boolesche oder ganzzahlige Variablen, die Sie an bestimmten Stellen mit einem Wert versehen und dann unten überprüfen oder dann weiter um etwas zu tun oder nicht, wie zum Beispiel mit
newItem = true
, dann einige Zeilen unterif (newItem ) then
- @MainMa Anscheinend da ' sa 3 .: Die Version mit 8 booleschen Argumenten ist das, woran ich dachte, als ich " flags " …
- Entschuldigung, aber meiner Meinung nach ist dies der perfekte Fall für die Methodenverkettung ( en.wikipedia.org/wiki/Method_chaining) a>), Zusätzlich können Sie ein Parameterarray verwenden (muss ein assoziatives Array oder eine Zuordnung sein), wobei jeder Eintrag in diesem Parameterarray, den Sie weglassen, das Standardwertverhalten verwendet für diesen Parameter. Am Ende kann der Aufruf über Methodenverkettung oder Parameterarrays so prägnant und ausdrucksstark sein wie Bitflags. Außerdem hat nicht jede Sprache Bitoperatoren (ich mag eigentlich binäre Flags, würde aber stattdessen die eben erwähnten Methoden verwenden).
- Es ' ist nicht sehr OOP, oder? Ich ' würde eine Schnittstelle ala erstellen: String myNewPassword = makePassword (randomComposeSupplier (neuer RandomLowerCaseSupplier (), neuer RandomUpperCaseSupplier (), neuer RandomNumberSupplier)); mit String makePassword (Lieferant < Zeichen > charSupplier); und Lieferant < Zeichen > randomComposeSupplier (Lieferant < Zeichen > … Lieferanten); Jetzt können Sie Ihre Lieferanten für andere Aufgaben wiederverwenden, sie nach Ihren Wünschen zusammenstellen und Ihre generatePassword-Methode so vereinfachen, dass nur ein minimaler Status verwendet wird.
- @Dibbeke Sprechen Sie über eine Königreich der Substantive …
Antwort
Ein riesiger Funktionsblock ist der Geruch , nicht die Fahnen. Wenn Sie das Flag in Zeile 5 setzen, dann suchen Sie nur in Zeile 354 nach dem Flag. Dann ist das schlecht. Wenn Sie das Flag in Zeile 8 setzen und in Zeile 10 nach dem Flag suchen, ist das in Ordnung. Außerdem sind ein oder zwei Flags pro Codeblock in Ordnung, 300 Flags in einer Funktion sind fehlerhaft.
Antwort
Normalerweise Flags kann vollständig durch eine Variante des Strategiemusters ersetzt werden, mit einer Strategieimplementierung für jeden möglichen Wert des Flags. Dies erleichtert das Hinzufügen neuen Verhaltens erheblich.
In leistungskritischen Situationen können die Kosten der Indirektion auftauchen und eine Dekonstruktion in eindeutige Flags erforderlich machen. Davon abgesehen habe ich Probleme, mich an einen einzelnen Fall zu erinnern, in dem ich das tatsächlich tun musste.
Antwort
Nein, Flags sind nicht schlecht oder ein Übel, das um jeden Preis überarbeitet werden muss.
Betrachten Sie Java Pattern.compile (String regex, int flags) anrufen. Dies ist eine traditionelle Bitmaske und es funktioniert. Werfen Sie einen Blick auf die -Konstanten in Java und überall dort, wo Sie eine Reihe von 2 n sehen, wissen Sie, dass dort Flags vorhanden sind.
In einer idealen überarbeiteten Welt würde man stattdessen ein EnumSet verwenden, bei dem die Konstanten stattdessen Werte in einer Aufzählung sind und wie in der Dokumentation angegeben:
Die räumliche und zeitliche Leistung dieser Klasse sollte gut genug sein, um als hochwertige, typsichere Alternative zu herkömmlichen int-basierten „Bit-Flags“ verwendet werden zu können.
In einer perfekten Welt wird dieser Pattern.compile-Aufruf zu Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags)
.
Alle das heißt, es ist immer noch Flaggen.Es ist viel einfacher, mit Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)
zu arbeiten, als mit Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline())
oder einer anderen Art zu versuchen, das zu tun, was Flags wirklich sind und gut für.
Flags werden häufig bei der Arbeit mit Dingen auf Systemebene angezeigt. Wenn eine Schnittstelle zu etwas auf Betriebssystemebene besteht, hat man wahrscheinlich irgendwo ein Flag – sei es der Rückgabewert eines Prozesses oder die Berechtigungen einer Datei oder die Flags zum Öffnen eines Sockets. Der Versuch, diese Instanzen bei einer Hexenjagd gegen einen wahrgenommenen Codegeruch umzugestalten, führt wahrscheinlich zu einem schlechteren Code, als wenn man die Flagge akzeptiert und verstanden hat.
Das Problem tritt auf, wenn Leute Flaggen missbrauchen, die sie zusammen werfen und Erstellen eines Frankenflag-Sets aller Arten von nicht verwandten Flags oder Versuch, sie dort zu verwenden, wo sie überhaupt keine Flags sind.
Antwort
Ich gehe davon aus, dass es sich um Flags innerhalb von Methodensignaturen handelt.
Die Verwendung eines einzelnen Flags ist schon schlimm genug.
Es bedeutet Ihren Kollegen nichts, wenn sie es sehen. Sie müssen sich den Quellcode der Methode ansehen, um festzustellen, was sie tut. Sie werden sich wahrscheinlich einige Monate später an derselben Position befinden, wenn Sie vergessen, worum es bei Ihrer Methode ging.
Das Übergeben eines Flags an die Methode bedeutet normalerweise, dass Ihre Methode für mehrere Dinge verantwortlich ist. Innerhalb der Methode führen Sie wahrscheinlich eine einfache Überprüfung der folgenden Zeilen durch:
if (flag) DoFlagSet(); else DoFlagNotSet();
Das ist eine schlechte Trennung von Bedenken und Sie können normalerweise einen Weg finden, um sie zu umgehen.
Normalerweise habe ich zwei separate Methoden:
public void DoFlagSet() { } public void DoFlagNotSet() { }
Dies ist sinnvoller bei Methodennamen, die für das zu lösende Problem gelten.
Das Übergeben mehrerer Flags ist doppelt so schlecht. Wenn Sie wirklich mehrere Flags übergeben müssen, sollten Sie sie in eine Klasse einkapseln. Selbst dann werden Sie immer noch mit dem gleichen Problem konfrontiert sein, da Ihre Methode wahrscheinlich mehrere Dinge tut.
Antwort
Flags und Die meisten Temperaturvariablen riechen stark. Höchstwahrscheinlich könnten sie überarbeitet und durch Abfragemethoden ersetzt werden.
Überarbeitet:
Flags und temporäre Variablen sollten beim Ausdrücken des Status in Abfragemethoden umgestaltet werden. Die Statuswerte (Boolesche Werte, Ints und andere Grundelemente) sollten als Teil der Implementierungsdetails fast immer ausgeblendet werden.
Flags, die für die Steuerung, das Routing und den allgemeinen Programmablauf verwendet werden, können auch das anzeigen Möglichkeit, Abschnitte der Kontrollstrukturen in separate Strategien oder Fabriken oder was auch immer situativ angemessen sein mag, umzugestalten, die weiterhin die Abfragemethoden verwenden.
Antwort
Wenn wir über Flags sprechen, sollten wir wissen, dass sie im Laufe der Programmausführung geändert werden und dass sie das Verhalten des Programms basierend auf ihren Zuständen beeinflussen. Solange wir die Kontrolle über diese beiden Dinge haben, funktionieren sie hervorragend.
Flags können hervorragend funktionieren, wenn
- Sie sie in einem geeigneten Bereich definiert haben. Mit „angemessen“ meine ich, dass der Bereich keinen Code enthalten sollte, der sie nicht ändern muss / sollte. Oder zumindest ist der Code sicher (z. B. kann er nicht direkt von außen aufgerufen werden).
- Wenn Flags von außen verarbeitet werden müssen und viele Flags vorhanden sind, können wir den Flag-Handler als einzigen Weg codieren um Flags sicher zu ändern. Dieser Flag-Handler kann selbst Flags und Methoden zum Ändern dieser Flaps kapseln. Es kann dann als Singleton erstellt und dann für Klassen freigegeben werden, die Zugriff auf Flags benötigen.
- Und schließlich zur Wartbarkeit, wenn zu viele Flags vorhanden sind:
- Sie müssen nicht sagen, dass sie sollten Befolgen Sie die sinnvolle Benennung.
- Sollte mit gültigen Werten dokumentiert werden (möglicherweise mit Aufzählungen).
- Sollte dokumentiert werden mit WELCHEM CODE ÄNDERT jeden von ihnen und auch mit WELCHEM ZUSTAND bei der Zuweisung eines bestimmten Werts zum Flag.
- WELCHER CODE VERBRAUCHT sie und WAS VERHALTEN ergibt sich für einen bestimmten Wert
Wenn es verdammt viele Flags gibt, sollte gute Designarbeit vorausgehen, da Flags dann eine Schlüsselrolle im Programmverhalten spielen. Sie können Zustandsdiagramme zur Modellierung verwenden. Solche Diagramme dienen auch als Dokumentation und visuelle Anleitung, wenn Sie mit ihnen umgehen.
Solange diese Dinge vorhanden sind, wird dies meiner Meinung nach nicht zu Unordnung führen.
Antwort
Ich ging von der Frage aus, dass die Qualitätssicherung Flag-Variablen (globale Variablen) und keine Bits eines Funktionsparameters bedeutet.
Es gibt Situationen wo Sie nicht viele andere Möglichkeiten haben. Zum Beispiel müssen Sie ohne Betriebssystem Interrupts auswerten.Wenn ein Interrupt sehr häufig auftritt und Sie keine Zeit haben, eine lange Auswertung im ISR durchzuführen, ist es nicht nur zulässig, sondern manchmal sogar eine bewährte Methode, nur einige globale Flags im ISR zu setzen (Sie sollten so wenig Zeit wie möglich aufwenden im ISR) und um diese Flags in Ihrer Hauptschleife auszuwerten.
Antwort
Ich glaube nicht, dass alles ist ein absolutes Übel in der Programmierung.
Es gibt eine andere Situation, in der Flags in Ordnung sein könnten, welche wurden hier noch nicht erwähnt …
Betrachten Sie die Verwendung von Verschlüssen in diesem Javascript-Snippet:
exports.isPostDraft = function ( post, draftTag ) { var isDraft = false; if (post.tags) post.tags.forEach(function(tag){ if (tag === draftTag) isDraft = true; }); return isDraft; }
Die übergebene innere Funktion für „Array.forEach“ kann nicht einfach „true zurückgeben“.
Daher müssen Sie den Status mit einem Flag außerhalb halten.