Kommentarer
Svar
Problemet jag har sett när jag underhåller kod som använder flaggor är att antalet stater växer snabbt och att det nästan alltid finns tillstånd som inte hanteras. Ett exempel från min egen erfarenhet: Jag arbetade med någon kod som hade dessa tre flaggor
bool capturing, processing, sending;
Dessa tre skapade åtta stater (det fanns faktiskt två andra flaggor också). Inte alla möjliga värdekombinationer täcktes av koden och användarna såg fel:
if(capturing && sending){ // we must be processing as well ... }
Det visade sig att det fanns situationer där antagandet i if-uttalandet ovan var falskt.
Flaggor tenderar att sammansättas över tiden och de döljer klassens faktiska tillstånd. Det är därför de bör undvikas.
Kommentarer
- +1, " bör undvikas ". Jag skulle lägga till något om ' men flaggor är nödvändiga i vissa situationer ' (vissa kanske säger ' ett nödvändigt ont ')
- @TrevorBoydSmith Enligt min erfarenhet är de inte, du behöver bara lite mer än den genomsnittliga hjärnkraft du skulle använda för en flagga
- I din examen borde det ha varit ett enda enum som representerar tillstånd, inte tre booléer.
- Du kanske kommer till ett liknande problem som jag ' står inför just nu. Utöver att täcka alla möjliga tillstånd kan två applikationer dela samma flagga (t.ex. ladda upp kunddata). I det här fallet kommer bara en uppladdare att använda flaggan, sätta av den och lycka till att hitta problemet i framtiden.
Svar
Här är ett exempel när flaggor är användbara.
Jag har en kodkod som genererar lösenord (med hjälp av en kryptografiskt säker pseudorandomnummergenerator). Metoden som ringer väljer om inte lösenordet ska innehålla stora bokstäver, små bokstäver, siffror, grundsymboler, utökade symboler, grekiska symboler, kyrilliska och unicode.
Med flaggor är det enkelt att kalla den här metoden:
var password = this.PasswordGenerator.Generate( CharacterSet.Digits | CharacterSet.LowercaseLetters | CharacterSet.UppercaseLetters);
och det kan till och med förenklas till:
var password = this.PasswordGenerator.Generate(CharacterSet.LettersAndDigits);
Utan flaggor, vad skulle metodesignaturen vara?
public byte[] Generate( bool uppercaseLetters, bool lowercaseLetters, bool digits, bool basicSymbols, bool extendedSymbols, bool greekLetters, bool cyrillicLetters, bool unicode);
kallas så här:
// 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);
Som noteras i kommentarerna skulle en annan metod vara att använda en samling:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LowercaseLetters, CharacterSet.UppercaseLetters, });
Detta är mycket mer läsbart jämfört med uppsättningen true
och false
, men har fortfarande två nackdelar:
Den största nackdelen är att för att tillåta kombinerade värden, som CharacterSet.LettersAndDigits
du skulle skriva något liknande i Generate()
metod:
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. }
kan eventuellt skrivas om så här:
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. }
Jämför detta med vad du har genom att använda flaggor:
if (set & CharacterSet.LowercaseLetters == CharacterSet.LowercaseLetters) { // The password should contain lowercase letters. }
s Ekonomisk mycket liten nackdel är att det inte är klart hur metoden skulle fungera om den skulle kallas så här:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LettersAndDigits, // So digits are requested two times. });
Kommentarer
- Jag tror att OP hänvisar till booleska eller heltalvariabler som du tilldelar ett värde på vissa platser och därefter kontrollerar du sedan i orther att göra något eller inte, som att till exempel använda
newItem = true
sedan några rader underif (newItem ) then
- @MainMa Tydligen där ' sa 3: Versionen med 8 booleska argument är vad jag tänkte på när jag läste " flaggor " …
- Tyvärr, men IMHO det här är det perfekta fallet för metodkedjning ( en.wikipedia.org/wiki/Method_chaining), Dessutom kan du använda en parametermatris (måste vara en associerande matris eller karta), där varje post i den parametermatrisen du utelämnar använder standardvärdesbeteendet för den parametern. I slutändan kan samtalet via metodkedjning eller parametermatriser vara lika kortfattade och uttrycksfulla som bitflaggor, inte alla språk har bitoperatorer (jag gillar faktiskt binära flaggor, men skulle använda de metoder jag just nämnde istället). / li>
- Det ' är inte särskilt OOP, eller hur? Jag ' Jag skapar ett gränssnitt ala: String myNewPassword = makePassword (randomComposeSupplier (new RandomLowerCaseSupplier (), new RandomUpperCaseSupplier (), new RandomNumberSupplier)); med sträng makePassword (leverantör < Tecken > charSupplier); och leverantör < Tecken > randomComposeSupplier (leverantör < Tecken > … leverantörer); Nu kan du återanvända dina leverantörer för andra uppgifter, komponera dem på vilket sätt du vill och förenkla din generationPassword-metod så att den använder minimalt tillstånd.
- @Dibbeke Prata om en substantivriket …
Svar
Ett enormt funktionsblock är lukten , inte flaggorna. Om du ställer in flaggan på rad 5, kolla bara efter flaggan på rad 354, då är det dåligt. Om du ställer in flaggan på rad 8 och söker efter flaggan på rad 10 är det bra. Dessutom är en eller två flaggor per kodblock bra, 300 flaggor i en funktion är dåliga.
Svar
Vanligtvis flaggar kan helt ersättas med en viss smak av strategimönstret, med en strategiimplementering för alla möjliga värden i flaggan. Detta gör det lättare att lägga till nytt beteende.
I prestandakritiska situationer kostar indirection kanske ytan och gör det nödvändigt att avkonstruera till tydliga flaggor. Med detta sagt har jag svårt att komma ihåg ett enda fall där jag faktiskt var tvungen att göra det.
Svar
Nej, flaggor är inte dåliga eller en ondska som måste omformas bort till varje pris.
Tänk på Java ”s Pattern.compile (String regex, int flags) samtal. Detta är en traditionell bitmask och den fungerar. Titta på konstanterna i java och var du än ser en massa 2 n vet du att det finns flaggor där.
I en ideal ombyggd värld skulle man istället använda en EnumSet där konstanterna istället är värden i enum och som dokumentationen lyder:
Utrymmes- och tidsprestanda för denna klass bör vara tillräckligt bra för att den ska kunna användas som ett högkvalitativt, typsäkert alternativ till traditionella int-baserade ”bitflaggor.”
I en perfekt värld blir det mönstret.compile-samtalet Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags)
.
Alla som sagt, det är fortfarande flaggor.Det är mycket lättare att arbeta med Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)
än det skulle vara att ha Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline())
eller någon annan typ av försök att göra vad flaggor verkligen är och bra för.
Flaggor ses ofta när man arbetar med saker på systemnivå. Vid gränssnitt med något på operativsystemsnivå är det troligt att man har en flagga någonstans – vare sig det är returvärdet för en process, eller behörigheterna för en fil eller flaggorna för att öppna ett uttag. Att försöka refaktorera dessa fall i någon häxjakt mot en uppfattad kodlukt kommer sannolikt att få sämre kod än om man använde accepterade och förstod flaggan.
Problemet uppstår när människor missbrukar flaggor som slänger dem ihop och skapa en frankenflag-uppsättning av alla typer av icke-relaterade flaggor eller försöka använda dem där de inte alls är flaggor.
Svar
Jag antar att vi pratar om flaggor inom metodunderskrifter.
Att använda en enda flagga är dåligt nog.
Det betyder ingenting för dina kollegor den första gången de ser det. De måste titta på källkoden för metoden för att fastställa vad den gör. Du kommer förmodligen att vara i samma position några månader längre fram, när du glömmer vad din metod handlade om.
Att skicka en flagga till metoden betyder normalt att din metod är ansvarig för flera saker. Inuti metoden gör du förmodligen en enkel kontroll på raderna av:
if (flag) DoFlagSet(); else DoFlagNotSet();
Det ”är en dålig åtskillnad mellan bekymmer och du kan normalt hitta en väg runt det.
Jag har normalt två separata metoder:
public void DoFlagSet() { } public void DoFlagNotSet() { }
Det här blir mer meningsfullt med metodnamn som är tillämpliga på problemet du löser.
Att skicka flera flaggor är dubbelt så dåligt. Om du verkligen behöver skicka flera flaggor än att överväga att kapsla in dem i en klass. Även då kommer du fortfarande att möta samma problem, eftersom din metod sannolikt gör flera saker.
Svar
Flaggor och de flesta tempvariabler är en stark lukt. Troligtvis kan de omformas och ersättas med frågemetoder.
Reviderad:
Flaggor och tempvariabler när de uttrycker tillstånd, bör omformuleras till frågemetoder. Tillståndsvärdena (booleaner, ints och andra primater) bör nästan-alltid döljas som en del av implementeringsdetaljerna.
Flaggor som används för kontroll, dirigering och allmänt programflöde kan också indikera möjlighet att omforma delar av kontrollstrukturerna till separata strategier eller fabriker, eller vad som helst som är situationellt lämpligt, som fortsätter att använda frågemetoderna.
Svar
När vi pratar om flaggor bör vi veta att de kommer att modifieras under programkörningstiden och att de kommer att påverka programmets beteende baserat på deras tillstånd. Så länge vi har snygg kontroll över dessa två saker kommer de att fungera bra.
Flaggor kan fungera bra om
- Du har definierat dem i ett lämpligt omfång. Med lämpligt menar jag att omfånget inte bör innehålla någon kod som inte behöver / borde inte ändra dem. Eller åtminstone är koden säker (till exempel kan den inte kallas direkt från utsidan)
- Om det finns behov av att hantera flaggor utifrån och om det finns många flaggor kan vi koda flagghanteraren som det enda sättet för att säkert modifiera flaggor. Den här flagghanteraren kan själv kapsla in flaggor och metoder för att modifiera dem. Det kan sedan göras singleton och kan sedan delas mellan klasser som behöver tillgång till flaggor.
- Och slutligen för underhållsförmåga, om det finns för många flaggor:
- Ingen anledning att säga att de borde följ förnuftig namngivning
- Bör dokumenteras med vad som är giltiga värden (kan vara med uppräkningar)
- Bör dokumenteras med VILKEN KOD FÖRÄNDRAR var och en av dem, och även med VILKET FÖRHÅLLANDE vid tilldelning av ett visst värde till flaggan.
- VILKEN KOD FÖRBRUKAR dem och VAD FÖRETAG kommer att resultera för ett visst värde
Om det finns en hel del flaggor bör bra designarbete föregås eftersom flaggor börjar spela en nyckelroll i programbeteendet. Du kan välja statliga diagram för modellering. Sådana diagram fungerar också som dokumentation och visuell vägledning när man hanterar dem.
Så länge dessa saker är på plats tror jag att det inte kommer att leda till röran.
Svar
Jag antog från frågan att QA menade flaggvariabler (globala) variabler och inte bitar av en funktionsparameter.
Det finns situationer där du inte har mycket andra möjligheter. Till exempel utan ett operativsystem måste du utvärdera avbrott.Om ett avbrott kommer mycket ofta och du inte har tid att göra en lång utvärdering i ISR, är det inte bara tillåtet utan ibland till och med bästa praxis att bara sätta några globala flaggor i ISR (du bör spendera så lite tid som möjligt i ISR) och för att utvärdera dessa flaggor i din huvudslinga.
Svar
Jag tror inte vad som helst är en absolut ondska i programmeringen, någonsin.
Det finns en annan situation där flaggor kan vara i ordning, vilket nämndes inte här ännu …
Tänk på användningen av stängningar i detta Javascript-kodavsnitt:
exports.isPostDraft = function ( post, draftTag ) { var isDraft = false; if (post.tags) post.tags.forEach(function(tag){ if (tag === draftTag) isDraft = true; }); return isDraft; }
Den inre funktionen, som passeras till ”Array.forEach”, kan inte bara ”returnera sant”.
Därför måste du hålla staten utanför med en flagga.
newItem = true
då några rader underif (newItem ) then