it-swarm.com.de

Sind Flaggenvariablen ein absolutes Übel?

Sind Flaggenvariablen böse? Sind die folgenden Variablen zutiefst unmoralisch und ist es böse, sie zu verwenden?

"Boolesche oder ganzzahlige Variablen, denen Sie an bestimmten Stellen einen Wert zuweisen und dann unten überprüfen, ob Sie etwas tun oder nicht, z. B. mit newItem = true, dann einige Zeilen unter if (newItem ) then"


Ich erinnere mich an einige Projekte, bei denen ich die Verwendung von Flags völlig vernachlässigt habe und am Ende eine bessere Architektur/einen besseren Code hatte. In anderen Projekten, an denen ich arbeite, ist dies jedoch üblich. Wenn Code wächst und Flags hinzugefügt werden, wächst auch IMHO-Code-Spaghetti.

Würden Sie sagen, dass es Fälle gibt, in denen die Verwendung von Flags eine gute oder sogar notwendige Praxis ist? Oder würden Sie zustimmen, dass die Verwendung von Flags im Code ... rote Flaggen sind und vermieden/überarbeitet werden sollten; Ich komme einfach damit zurecht, Funktionen/Methoden auszuführen, die stattdessen in Echtzeit nach Zuständen suchen.

49
dukeofgaming

Das Problem, das ich beim Verwalten 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 gearbeitet, der diese drei Flags hatte

bool capturing, processing, sending;

Diese drei schufen acht Zustände (tatsächlich gab es auch zwei andere Flaggen). 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 obigen if-Aussage falsch war.

Flaggen neigen dazu, sich im Laufe der Zeit zu verstärken, und sie verbergen den tatsächlichen Zustand einer Klasse. Deshalb sollten sie vermieden werden.

42
Ben

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 das Passwort Großbuchstaben, Kleinbuchstaben, Ziffern, Grundsymbole, erweiterte Symbole, griechische Symbole, kyrillische Symbole und Unicode enthalten soll oder nicht.

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, um:

var password = this.PasswordGenerator.Generate(CharacterSet.LettersAndDigits);

Was wäre ohne Flags die Methodensignatur?

public byte[] Generate(
    bool uppercaseLetters, bool lowercaseLetters, bool digits, bool basicSymbols,
    bool extendedSymbols, bool greekLetters, bool cyrillicLetters, bool unicode);

so genannt:

// 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 ist, dass Sie, um kombinierte Werte wie CharacterSet.LettersAndDigits Zuzulassen, so etwas in der Methode Generate() schreiben würden:

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

Der zweite, sehr kleine Nachteil ist, dass nicht klar ist, wie sich die Methode verhalten würde, wenn sie so aufgerufen würde:

var password = this.PasswordGenerator.Generate(
    new []
    {
        CharacterSet.Digits,
        CharacterSet.LettersAndDigits, // So digits are requested two times.
    });
38

Ein riesiger Funktionsblock ist der Geruch, nicht die Fahnen. Wenn Sie das Flag in Zeile 5 setzen und dann nur in Zeile 354 nach dem Flag suchen, 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 schlecht.

15
nbv4

Normalerweise können Flags vollständig durch eine Variante des Strategiemusters ersetzt werden, wobei für jeden möglichen Wert des Flags eine Strategieimplementierung erforderlich ist. Dies erleichtert das Hinzufügen neuen Verhaltens erheblich.

In leistungskritischen Situationen tauchen die Kosten der Indirektion möglicherweise auf und machen eine Dekonstruktion in klare Flags erforderlich. Davon abgesehen habe ich Probleme, mich an einen einzigen Fall zu erinnern, in dem ich das tatsächlich tun musste.

10
back2dos

Nein, Flaggen sind nicht schlecht oder ein Übel, das um jeden Preis umgestaltet werden muss.

Betrachten Sie den Aufruf von Java Pattern.compile (String regex, int flags) . Dies ist eine traditionelle Bitmaske und es funktioniert. Werfen Sie einen Blick auf die Konstanten in Java und wo immer Sie ein Bündel von 2 sehenn Sie wissen, dass dort Flaggen sind.

In einer idealen überarbeiteten Welt würde man stattdessen ein EnumSet verwenden, wobei die Konstanten stattdessen Werte in einer Aufzählung sind und wie in der Dokumentation lautet:

Die räumliche und zeitliche Leistung dieser Klasse sollte gut genug sein, um ihre Verwendung als hochwertige, typsichere Alternative zu herkömmlichen int-basierten "Bit-Flags" zu ermöglichen.

In einer perfekten Welt wird dieser Pattern.compile-Aufruf zu Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags).

Alles was gesagt wurde, es sind 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 wofür sie gut sind.

Bei der Arbeit mit Dingen auf Systemebene treten häufig Flags auf. 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 zu überarbeiten, wird wahrscheinlich zu einem schlechteren Code führen, als wenn man die Flagge akzeptiert und verstanden hätte.

Das Problem tritt auf, wenn Leute Flaggen missbrauchen, die sie zusammenwerfen und ein Frankenflag-Set aller Arten von nicht verwandten Flaggen erstellen oder versuchen, sie dort zu verwenden, wo sie überhaupt keine Flaggen sind.

6
user40980

Ich gehe davon aus, dass es sich um Flags in Methodensignaturen handelt.

Die Verwendung einer einzelnen Flagge ist schon schlimm genug.

Es wird Ihren Kollegen nichts bedeuten, wenn sie es sehen. Sie müssen sich den Quellcode der Methode ansehen, um festzustellen, was sie tut. Sie werden wahrscheinlich einige Monate später in der gleichen Position sein, 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 verschiedene 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.

5
CodeART

Flags und die meisten Temp-Variablen 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 Zustandswerte (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 die Möglichkeit anzeigen, Abschnitte der Steuerungsstrukturen in separate Strategien oder Fabriken umzugestalten, oder was auch immer situativ angemessen sein mag, die weiterhin die Abfragemethoden verwenden.

3
JustinC

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, werden sie großartig funktionieren.

Flaggen können gut funktionieren, wenn

  • Sie haben sie in einem geeigneten Bereich definiert. Mit "angemessen" meine ich, dass der Bereich keinen Code enthalten sollte, der sie nicht ändern muss/sollte. Oder zumindest ist der Code sicher (zum Beispiel 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 einzige Möglichkeit zum sicheren Ändern von Flags codieren. 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 einer vernünftigen Benennung folgen sollten
    • 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 wird dem Flag ein bestimmter Wert zugewiesen.
    • 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 sich mit ihnen befassen.

Solange diese Dinge vorhanden sind, denke ich, wird es nicht zu dem Durcheinander führen.

2
Maha

Ich ging von der Frage aus, dass die Qualitätssicherung Flag-Variablen (globale Variablen) und keine Bits eines Funktionsparameters bedeutet.

Es gibt Situationen, in denen Sie nicht viele andere Möglichkeiten haben. Ohne Betriebssystem müssen Sie beispielsweise 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.

1
vsz

Ich denke nicht, dass irgendetwas jemals ein absolutes Übel in der Programmierung ist.

Es gibt eine andere Situation, in der möglicherweise Flaggen in Ordnung sind, die hier noch nicht erwähnt wurden ...

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 innere Funktion, die an "Array.forEach" übergeben wird, kann nicht einfach "true zurückgeben".

Daher müssen Sie den Staat mit einer Flagge draußen halten.

0
firstdoit