Chiuso . Questa domanda deve essere più mirata . Attualmente non accetta risposte.

Commenti

  • Sembra che MainMa e io abbiamo una definizione diversa di " flag. " Stavo pensando al preprocessore #ifdefs. Di quale stavi chiedendo?
  • Questa è davvero una buona domanda. Mi ' me lo chiedevo molto e mi sono ritrovato a dire " oh, beh, basta usare una bandiera " un po troppo.
  • I booleani sono flag. (Quindi sono interi, così sono …)
  • @KarlBielefeldt Credo che OP si riferisca a variabili booleane o intere a cui si assegna un valore in determinati punti, quindi in basso si controlla poi in o in altre per fare qualcosa o non, come, ad esempio, usare newItem = true quindi alcune righe sotto if (newItem ) then
  • Considera anche introduce il refactoring della variabile esplicativa in questo contesto. Finché il metodo rimane breve e ha un basso numero di percorsi attraverso di esso, lo considero un uso valido.

Answer

Il problema che ho riscontrato durante la manutenzione del codice che utilizza i flag è che il numero di stati cresce rapidamente e ci sono quasi sempre stati non gestiti. Un esempio tratto dalla mia esperienza personale: stavo lavorando su un codice con questi tre flag

bool capturing, processing, sending; 

Questi tre hanno creato otto stati (in realtà, cerano altri due flag anche). Non tutte le possibili combinazioni di valori erano coperte dal codice e gli utenti vedevano dei bug:

if(capturing && sending){ // we must be processing as well ... } 

Si è scoperto che cerano situazioni in cui lassunto nellistruzione if sopra era falso.

I flag tendono a comporsi nel tempo e nascondono lo stato effettivo di una classe. Questo è il motivo per cui dovrebbero essere evitati.

Commenti

  • +1, " dovrebbero essere evitati ". Vorrei aggiungere qualcosa su ' ma in alcune situazioni sono necessarie le bandiere ' (alcuni potrebbero dire ' un male necessario ')
  • @TrevorBoydSmith Nella mia esperienza non lo sono, hai solo bisogno di un po più della potenza cerebrale media che useresti per una bandiera
  • Nel tuo esempio avrebbe dovuto essere un singolo enum che rappresenta lo stato, non 3 booleani.
  • Potresti arrivare a un problema simile a quello che ho ' Sono rivolto proprio ora. Oltre a coprire tutti gli stati possibili, due applicazioni potrebbero condividere lo stesso flag (ad es. Caricamento dei dati dei clienti). In questo caso, solo un Uploader utilizzerà il flag, lo disattiverà e buona fortuna per trovare il problema in futuro.

Risposta

Ecco un esempio in cui i flag sono utili.

Ho un pezzo di codice che genera password (utilizzando un generatore di numeri pseudocasuali crittograficamente sicuro). Il chiamante del metodo sceglie se o non la password dovrebbe contenere lettere maiuscole, minuscole, cifre, simboli di base, simboli estesi, simboli greci, cirillici e unicode.

Con i flag, chiamare questo metodo è facile:

 var password = this.PasswordGenerator.Generate( CharacterSet.Digits | CharacterSet.LowercaseLetters | CharacterSet.UppercaseLetters);  

e può anche essere semplificato in:

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

Senza flag, quale sarebbe la firma del metodo?

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

chiamato così:

 // 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);  

Come notato nei commenti, un altro approccio potrebbe essere quello di utilizzare una raccolta:

 var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LowercaseLetters, CharacterSet.UppercaseLetters, });  

Questo è molto più leggibile rispetto al set di true e false, ma ha ancora due svantaggi:

Il principale svantaggio è che per consentire valori combinati, come CharacterSet.LettersAndDigits scriveresti qualcosa del genere nel Generate() metodo:

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

possibilmente riscritto in questo modo:

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

Confronta questo con quello che hai utilizzando i flag:

 if (set & CharacterSet.LowercaseLetters == CharacterSet.LowercaseLetters) { // The password should contain lowercase letters. }  

Il S Inoltre, uno svantaggio molto minore è che non è chiaro come si comporterebbe il metodo se chiamato in questo modo:

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

Commenti

  • Credo che OP si riferisca a variabili booleane o intere a cui si assegna un valore in determinati punti, quindi in basso controlli poi in altre fare qualcosa o no, come, ad esempio, usare newItem = true poi alcune righe sotto if (newItem ) then
  • @MainMa Apparentemente lì ' sa 3 °: la versione con 8 argomenti booleani è ciò a cui ho pensato quando ho letto " flags " …
  • Mi dispiace, ma IMHO questo è il caso perfetto per il concatenamento di metodi ( en.wikipedia.org/wiki/Method_chaining), Inoltre, puoi usare un array di parametri (deve essere un array o una mappa associativa), dove ogni voce in quellarray di parametri che ometti usa il comportamento del valore predefinito per quel parametro. Alla fine, la chiamata tramite il concatenamento di metodi o gli array di parametri può essere succinta ed espressiva come i flag di bit, inoltre, non tutte le lingue hanno operatori di bit (in realtà mi piacciono i flag binari, ma invece userei i metodi che ho appena menzionato).
  • ' non è molto OOP, vero? ' d creare uninterfaccia come: String myNewPassword = makePassword (randomComposeSupplier (new RandomLowerCaseSupplier (), new RandomUpperCaseSupplier (), new RandomNumberSupplier)); con stringa makePassword (fornitore < carattere > charSupplier); e Fornitore < Carattere > randomComposeSupplier (Fornitore < Carattere > … fornitori); Ora puoi riutilizzare i tuoi fornitori per altre attività, comporli nel modo che preferisci e semplificare il tuo metodo generatePassword in modo che utilizzi uno stato minimo.
  • @Dibbeke Talk about a regno dei nomi

Risposta

Un enorme blocco funzione è lodore , non le bandiere. Se imposti la bandiera sulla riga 5, controlla solo la bandiera sulla riga 354, allora non va bene. Se imposti la bandiera sulla riga 8 e controlli la bandiera sulla riga 10, va bene. Inoltre, uno o due flag per blocco di codice vanno bene, 300 flag in una funzione sono cattivi.

Answer

Di solito flag può essere completamente sostituito da qualche tipo di modello di strategia, con unimplementazione della strategia per ogni possibile valore della bandiera. Ciò rende molto più facile laggiunta di nuovi comportamenti.

In situazioni critiche per le prestazioni il costo dellindirizzamento indiretto potrebbe emergere e rendere necessaria la decostruzione in flag chiari. Detto questo, “ho problemi a ricordare un singolo caso in cui dovevo effettivamente farlo.

Answer

No, le bandiere non sono cattive o cattive che devono essere modificate a tutti i costi.

Considera Java “s Pattern.compile (String regex, int flags) chiamata. Questa è una maschera bit tradizionale e funziona. Dai unocchiata alle costanti in java e ovunque vedi un gruppo di 2 n sai che ci sono delle bandiere.

In un mondo refactored ideale, si utilizzerebbe invece un EnumSet dove le costanti sono invece valori in un enum e come si legge nella documentazione:

Le prestazioni in termini di spazio e tempo di questa classe dovrebbero essere sufficientemente buone da consentirne luso come alternativa di alta qualità e sicura per i tipi ai tradizionali “bit flag” basati su int.

In un mondo perfetto, quella chiamata Pattern.compile diventa Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags).

Tutto detto questo, è ancora bandiere.È molto più facile lavorare con Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE) di quanto lo sarebbe con Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline()) o altri modi per provare a fare ciò che sono realmente i flag e buono per.

I flag sono spesso visti quando si lavora con cose a livello di sistema. Quando ci si interfaccia con qualcosa a livello di sistema operativo, è probabile che si abbia un flag da qualche parte, che si tratti del valore di ritorno di un processo, dei permessi di un file o dei flag per lapertura di un socket. Cercare di effettuare il refactoring di queste istanze in una caccia alle streghe contro un odore di codice percepito probabilmente finirà con un codice peggiore rispetto a se uno usato accettasse e comprendesse la bandiera.

Il problema si verifica quando le persone usano male le bandiere gettandole insieme creando un insieme di flag di frankenflag di tutti i tipi di flag non correlati o tentando di usarli dove non sono affatto flag.

Answer

Presumo che stiamo parlando di flag allinterno delle firme dei metodi.

Usare un singolo flag è già abbastanza brutto.

Non significherà nulla per i tuoi colleghi la prima volta che lo vedranno. Dovranno esaminare il codice sorgente del metodo per stabilire cosa fa. Probabilmente sarai nella stessa posizione pochi mesi dopo, quando dimenticherai di cosa trattava il tuo metodo.

Passare un flag al metodo, normalmente significa che il tuo metodo è responsabile di più cose. Allinterno del metodo probabilmente stai facendo un semplice controllo sulle righe di:

if (flag) DoFlagSet(); else DoFlagNotSet(); 

Questa “è una scarsa separazione delle preoccupazioni e normalmente puoi trovare un modo per aggirarla.

Normalmente ho due metodi separati:

public void DoFlagSet() { } public void DoFlagNotSet() { } 

Questo avrà più senso con i nomi dei metodi applicabili al problema che stai risolvendo.

Passare più flag è due volte meno dannoso. Se hai davvero bisogno di passare più flag, considera di incapsularli allinterno di una classe. Anche in questo caso, dovrai comunque affrontare lo stesso problema, poiché il tuo metodo probabilmente sta facendo più cose.

Answer

Flag e la maggior parte delle variabili di temperatura sono un odore forte. Molto probabilmente potrebbero essere riformattati e sostituiti con metodi di query.

Rivisto:

I flag e le variabili temporanee quando esprimono lo stato, dovrebbero essere refactored nei metodi di query. I valori di stato (booleani, int e altre primarie) dovrebbero quasi -sempre- essere nascosti come parte dei dettagli di implementazione.

I flag utilizzati per il controllo, linstradamento e il flusso generale del programma possono anche indicare il opportunità di refactoring sezioni delle strutture di controllo in strategie o fabbriche separate, o qualsiasi altra cosa potrebbe essere appropriata in base alla situazione, che continuano a fare uso dei metodi di query.

Risposta

Quando parliamo di flag, dobbiamo sapere che verranno modificati nel tempo di esecuzione del programma e che influenzeranno il comportamento del programma in base ai loro stati. Finché abbiamo un controllo preciso su queste due cose, funzioneranno alla grande.

Le bandiere possono funzionare alla grande se

  • Le hai definite in uno scope appropriato. Con appropriato intendo che lambito non deve contenere alcun codice che non debba / non debba modificarli. O almeno il codice è sicuro (ad esempio potrebbe non essere chiamato direttamente dallesterno)
  • Se è necessario gestire i flag dallesterno e se ci sono molti flag possiamo codificare il flag handler come unico modo per modificare in sicurezza i flag. Questo gestore di flag può esso stesso incapsulare flag e metodi per modificarli. Può quindi essere reso singleton e può essere quindi condiviso tra le classi che necessitano dellaccesso ai flag.
  • E infine per la manutenibilità, se ci sono troppe flag:
    • Non cè bisogno di dire che dovrebbero seguire una denominazione sensata
    • Dovrebbe essere documentato con quelli che sono valori validi (potrebbe essere con enumerazioni)
    • Dovrebbe essere documentato con QUALE CODICE MODIFICERÀ ciascuno di essi, e anche CON QUALE CONDIZIONE risulterà nellassegnazione di un particolare valore al flag.
    • QUALE CODICE LE CONSUMERÀ e CHE COMPORTAMENTO risulterà per un particolare valore

Se ci sono un sacco di flag, un buon lavoro di progettazione dovrebbe precedere da quando i flag iniziano a giocare un ruolo chiave nel comportamento del programma. Puoi andare per i diagrammi di stato per la modellazione. Tali diagrammi funzionano anche come documentazione e guida visiva mentre li gestisci.

Finché queste cose sono a posto, penso che non porteranno al caos.

Risposta

Dalla domanda ho assunto che il QA significasse variabili flag (globali) e non bit di un parametro di funzione.

Ci sono situazioni dove non hai molte altre possibilità. Ad esempio, senza un sistema operativo devi valutare gli interrupt.Se un interrupt arriva molto frequentemente e non hai tempo per fare una lunga valutazione nellISR, non soloèconsentito ma a volte è anche buona pratica impostare solo alcuni flag globali nellISR (dovresti dedicare il minor tempo possibile nellISR) e per valutare questi flag nel ciclo principale.

Risposta

Non credo che qualsiasi cosa è un male assoluto nella programmazione, mai.

Cè unaltra situazione in cui i flag potrebbero essere in ordine, che non sono stati ancora menzionati qui …

Considera luso di chiusure in questo snippet Javascript:

exports.isPostDraft = function ( post, draftTag ) { var isDraft = false; if (post.tags) post.tags.forEach(function(tag){ if (tag === draftTag) isDraft = true; }); return isDraft; } 

La funzione interna, essendo passata a “Array.forEach”, non può semplicemente “restituire true”.

Quindi, è necessario mantenere lo stato esterno con una bandiera.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *