Bayrak değişkenleri mutlak bir kötülük mü? [kapalı]


47

Bayrak değişkenleri kötü mü? Aşağıdaki değişkenler derin ahlaksız mıdır ve bunları kullanmak kötü mü?

"alttan belirli yerlere bir değer atadığınız boolean veya integer değişkenleri, daha sonra aşağıdan bir şeyler yapıp yapmadığınızı veya başka bir deyişle aşağıdaki newItem = truesatırları kullanıp kullanmadığınızı kontrol edin. if (newItem ) then"


Bayrakları kullanmayı tamamen ihmal ettiğim ve daha iyi mimari / kod yazdığım birkaç proje yaptığımı hatırlıyorum; Ancak, çalıştığım diğer projelerde yaygın bir uygulamadır ve kod büyüdüğünde ve bayraklar eklendiğinde, IMHO kod spagetti de büyür.

Bayrakları kullanmanın iyi bir uygulama olduğu veya hatta gerekli olduğu durumlar olduğunu söyler misiniz, yoksa kodlarda bayrak kullanmanın ... kırmızı bayraklar olduğu ve bundan kaçınılması / düzeltilmesi gerektiğine katılıyor musunuz? Ben sadece gerçek zamanlı olarak durumları kontrol eden fonksiyonlar / yöntemler yaparak uğraşıyorum.


9
Öyle görünüyor ki MainMa ve ben farklı bir "bayrak" tanımına sahibiz. İşlemci #ifdefs'i düşünüyordum. Hangisini soruyordun?
Karl Bielefeldt

Bu gerçekten iyi bir soru. Bunu kendim için çok merak ettim ve kendimi gerçekten "çok fazla bir bayrak kullanalım" diyerek buldum.
Paul Richter

Booleanlar bayraklardır. (Yani tam sayılar, yani ...)
Thomas Eding

7
@KarlBielefeldt OP'nin belirli yerlere bir değer atadığınız boolean veya integer değişkenleri kastettiğine inanıyorum, sonra aşağıdan sonra bir şey yapıp yapmadığınızı kontrol edip, newItem = truesonradan aşağıdaki bazı satırları kullanarakif (newItem ) then
Tulains Córdova

1
Ayrıca bu bağlamda değişken yeniden yapılandırma açıklamasını da tanıtın. Metot kısa kaldığı ve içinden az sayıda yol olduğu sürece bunun geçerli bir kullanım olduğunu düşünüyorum.
Daniel B

Yanıtlar:


41

Bayrakları kullanan kodları korurken gördüğüm sorun, devlet sayısının hızlı bir şekilde artması ve neredeyse her zaman işlenmemiş durumların olması. Kendi tecrübelerime bir örnek: Bu üç bayraktan oluşan bazı kodlar üzerinde çalışıyordum.

bool capturing, processing, sending;

Bu üç sekiz devlet yarattı (aslında, başka iki bayrak da vardı). Tüm olası değer kombinasyonları kodda ele alınmadı ve kullanıcılar hata görüyordu:

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

Anlaşılan yukarıdaki if ifadesindeki varsayımın yanlış olduğu durumlar ortaya çıktı.

Bayraklar zamanla birleşme eğilimindedir ve sınıfın gerçek durumunu gizlerler. Bu yüzden kaçınılması gerekir.


3
+1, "kaçınılmalı". 'Ama bazı durumlarda bayraklar gerekli' (bazıları 'gerekli bir kötülük' diyebilir) hakkında bir şeyler
eklerdim

2
@TrevorBoydSmith Deneyimlerime göre onlar değil, sadece bir bayrak için kullanacağın ortalama beyin gücünden biraz daha fazlasını gerektiriyor
dukeofgaming

Senin sınavında 3 booleanı değil durumu temsil eden tek bir numara olmalıydı.
user949300,

Şu anda karşılaştığım şeye benzer bir soruna gelebilirsin. Tüm olası durumları karşılamanın yanı sıra, iki uygulama aynı bayrağı paylaşabilir (örneğin, müşteri verilerini yükleme). Bu durumda, yalnızca bir Yükleyici bayrağı kullanacak, yola koyulacak ve gelecekte sorunu bulma konusunda iyi şanslar sağlayacaktır.
Alan,

38

Bayrakların kullanışlı olduğu bir örnek.

Şifreler üreten bir kod parçam var (şifreli olarak güvenli bir sözde rasgele sayı üreteci kullanarak). Yöntemin arayanı, şifrenin büyük harfler, küçük harfler, rakamlar, temel semboller, genişletilmiş semboller, Yunanca semboller, Kiril harfleri ve unicode içermesi gerektiğini seçer.

Bayraklarla bu yöntemi çağırmak kolaydır:

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

ve hatta için basitleştirilmiş olabilir:

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

Bayraklar olmadan, yöntem imzası ne olurdu?

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

böyle denir:

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

Yorumlarda belirtildiği gibi, başka bir yaklaşım bir koleksiyon kullanmak olacaktır:

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

Bu setine kıyasla çok daha okunabilir trueve falsefakat yine de iki dezavantajları vardır:

En büyük dezavantajı, birleştirilmiş değerlere izin vermek için, CharacterSet.LettersAndDigitssizin gibi Generate()yöntemde şöyle bir şeyler yazmanızdır :

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

muhtemelen şöyle yeniden yazılmıştı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.
}

Bayrakları kullanarak bunu sahip olduklarınızla karşılaştırın:

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

İkincisi, çok küçük dezavantajı, böyle çağrılırsa yöntemin nasıl davranacağının açık olmamasıdır:

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

10
OP'nin bazı yerlerde bir değer atadığınız boolean veya integer değişkenleri kastettiğine inanıyorum, sonra aşağıdan sonra bir şey yapıp yapmadığınızı kontrol edersiniz, örneğin newItem = trueaşağıdaki satırları kullanarakif (newItem ) then
Tulains Córdova

1
@MainMa Görünüşe göre bir 3. var: 8 boolean argümanı olan sürüm "bayraklar" okuduğumda düşündüklerim ...
Izkata

4
Üzgünüz, ancak IMHO bu yöntem zincirleme için mükemmel bir durumdur ( en.wikipedia.org/wiki/Method_chaining ), Ek olarak, bir parametre dizisini (bir ilişkisel dizi veya harita olması gerekir) kullanabilirsiniz, burada bu parametre dizisindeki herhangi bir giriş ihmal edersiniz, o parametre için varsayılan değer davranışını kullanır. Sonunda, yöntem zincirleme veya parametre dizileri üzerinden yapılan çağrı bit bayrakları kadar özlü ve etkileyici olabilir, ayrıca her dilin bit operatörleri olmaz (aslında ikili bayrakları severim, ancak az önce bahsettiğim yöntemleri kullanırdım).
dukeofgaming

3
Çok fazla OOP değil, değil mi? Ala bir arayüz alacağım: String myNewPassword = makePassword (randomComposeSupplier (yeni RandomLowerCaseSupplier (), yeni RandomUpperCaseSupplier (), yeni RandomNumberSupplier)); String makePassword ile (Tedarikçi <Character> charSupplier); ve Tedarikçi <Character> randomComposeSupplier (Tedarikçi <Character> ... tedarikçileri); Artık tedarikçilerinizi diğer görevler için yeniden kullanabilir, istediğiniz şekilde oluşturabilir ve şuanda en düşük durumu kullanması için construcPadword yönteminizi basitleştirebilirsiniz.
Dibbeke

4
@Dibbeke İsimlerin krallığı hakkında konuşun ...
Phil

15

Büyük bir fonksiyon bloğu bayraklar değil kokudur. Bayrağı 5. satıra ayarlarsanız, sadece 354 hattındaki bayrağa bakın, o zaman bu kötüdür. Bayrağı 8 çizgisine ayarlayıp 10 çizgisindeki bayrağa bakarsanız, sorun değil. Ayrıca, kod bloğu başına bir veya iki bayrak iyi, bir işlevdeki 300 bayrak bozuk.


10

Genellikle bayraklar, strateji deseninin bazı lezzetleriyle tamamen değiştirilebilir, bayrağın her olası değeri için tek bir strateji uygulaması yapılabilir. Bu, yeni davranış eklemeyi çok daha kolaylaştırır.

Performans kritik durumlarda dolaylama maliyeti olabilir yüzey ve net bayrakları içine yapısöküm gerekli kılıyor. Olduğu söyleniyor, aslında bunu yapmak zorunda kaldığım tek bir vakayı hatırlamakta güçlük çekiyorum.


6

Hayır, bayraklar kötü değildir ya da her ne pahasına olursa olsun ortadan kaldırılması gereken bir kötülük değildir.

Java'nın Pattern.compile (String regex, int flags) çağrısını göz önünde bulundurun . Bu geleneksel bir bit maskesidir ve çalışır. En Bakış sabitleri java ve 2 'lik bir demet gördükleri her yerde n orada olmak bayraklar olduğunu biliyoruz.

İdeal bir yeniden yapılandırılmış dünyada, bunun yerine bir EnumSet kullanılır, burada sabitler bir enumdaki değerler yerine geçer ve belgeler okur:

Bu sınıfın uzay ve zaman performansı, geleneksel int tabanlı "bit bayraklara" yüksek kaliteli, güvenli bir alternatif olarak kullanılmasına izin verecek kadar iyi olmalıdır.

Mükemmel bir dünyada, bu Pattern.compile çağrısı olur Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags).

Bütün bunlar, yine de bayrakları. Birlikte çalışmak, bayrakların gerçekte ne için ve ne işe yaradığını yapmaya çalışmaktan başka bir tarz veya Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)olacağından daha kolaydır Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline()).

Bayraklar genellikle sistem seviyesinde işler ile çalışırken görülür. İşletim sistemi düzeyinde bir şey ile etkileşime girerken, birinin bir yerde bir bayrağı olması muhtemeldir - bir işlemin geri dönüş değeri, bir dosyanın izinleri veya bir soketi açmak için kullanılan bayraklar. Algılanan bir kod kokusuna karşı bir cadı avında bu örnekleri uzaklaştırmaya çalışmak, muhtemelen bir bayrağı kabul edip anlamadan daha kötü bir kodla sonuçlanacaktır.

Sorun, insanlar bayraklarını bir araya getirip kötüye kullanmaya başladığında ve her türlü ilgisiz bayraktan oluşan bir frankenflag grubu oluştururken veya onları bayrak olmayan yerlerde kullanmaya çalışırken ortaya çıkar.


5

Sanırım yöntem imzalarındaki bayraklardan bahsediyoruz.

Tek bir bayrak kullanmak yeterince kötü.

Meslektaşlarınız için gördükleri ilkeler zamanı hiçbir şey ifade etmez. Ne yaptığını belirlemek için yöntemin kaynak koduna bakmak zorunda kalacaklar. Muhtemelen yönteminizin ne hakkında olduğunu unuttuğunuzda, hatta birkaç ay boyunca aynı pozisyonda olacaksınız.

Yönteme bir bayrak iletmek, normalde yönteminizin birden fazla şeyden sorumlu olduğu anlamına gelir. Yöntemin içinde muhtemelen şu satırları basit bir şekilde kontrol ediyorsunuz:

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

Bu, endişelerin kötü bir şekilde ayrılmasıdır ve normal olarak bunun çevresinde bir yol bulabilirsiniz.

Normalde iki ayrı yöntemim var:

public void DoFlagSet() 
{
}

public void DoFlagNotSet()
{
}

Bu, çözmekte olduğunuz problem için geçerli olan yöntem adlarıyla daha anlamlı olacaktır.

Birden fazla bayrak geçmek iki kat daha kötü. Eğer gerçekten birden fazla bayrak geçmeniz gerekiyorsa, onları bir sınıf içinde ele almayı düşünün. O zaman bile, yönteminiz muhtemelen birden fazla şey yapıyorsa, hala aynı sorunla karşı karşıya kalacaksınız.


3

Bayraklar ve çoğu sıcaklık değişkenleri güçlü bir koku. Büyük olasılıkla onlar yeniden yapılandırılabilir ve sorgu yöntemleri ile değiştirilebilir.

Revize:

Durum ifade edilirken bayraklar ve geçici değişkenler, sorgu yöntemlerine yeniden yansıtılmalıdır. Devlet değerleri (boolean, ints ve diğer ilkel değerler) uygulama detaylarının bir parçası olarak neredeyse her zaman gizlenmelidir.

Kontrol, yönlendirme ve genel program akışı için kullanılan bayraklar, kontrol yapılarının bölümlerini ayrı stratejilere veya fabrikalara veya durumsal olarak uygun olan ne olursa olsun, sorgu yöntemlerini kullanmaya devam edecek şekilde yeniden ayırma fırsatını gösterebilir.


2

Bayraklar hakkında konuştuğumuzda, programın yürütülmesi sırasında değiştirileceklerini ve programın durumlarını temel alan davranışlarını etkileyeceklerini bilmeliyiz. Bu iki şey üzerinde düzgün bir kontrolümüz olduğu sürece, harika çalışacaklar.

Bayraklar eğer iyi çalışırsa

  • Onları uygun bir kapsamda tanımladınız. Uygun derken, kapsamın onları değiştirmesi gereken / değiştirmemesi gereken herhangi bir kod içermemesi gerektiği anlamına gelir. Veya en azından kod güvenli (örneğin doğrudan dışarıdan aranamayabilir)
  • Bayrakları dışarıdan ele almak gerekirse ve çok sayıda bayrak varsa, bayrak işleyicisini bayrakları güvenli bir şekilde değiştirmenin tek yolu olarak kodlayabiliriz. Bu bayrak işleyicisinin kendisi bayrakları ve bunları değiştirme yöntemlerini kapsüllenebilir. Daha sonra tekil hale getirilebilir ve daha sonra bayraklara erişmesi gereken sınıflar arasında paylaşılabilir.
  • Ve nihayetinde çok fazla bayrak varsa, sürdürülebilirlik için:
    • Mantıklı isimleri takip etmeleri gerektiğini söylemeye gerek yok
    • Geçerli değerler ile belgelendirilmelidir (numaralandırmalı olabilir)
    • Her biri NEDEN KODU DEĞİŞTİRECEKTİRİLMELİDİR ve ayrıca NEDEN DURUMDA bayrakla belirli bir değerin atanmasına neden olacaktır.
    • NEDEN KOD onları TÜKETECEKTİR ve NE ZAMAN DAVRANIŞ belirli bir değere neden olur

Eğer çok sayıda bayrak varsa, bayraklardan bu yana iyi tasarım çalışmaları yapılmalıdır, daha sonra program davranışında anahtar rol oynamaya başlar. Modelleme için durum diyagramlarına gidebilirsiniz. Bu tür diyagramlar ayrıca onlarla ilgilenirken dokümantasyon ve görsel rehberlik olarak da çalışır.

Bu şeyler olduğu sürece karmaşaya yol açmayacağını düşünüyorum.


1

QA'nın flag (global) değişkenleri anlamına geldiğini ve bir fonksiyon parametresinin bitleri olmadığı sorusundan varsaydım.

Başka olanakların olmadığı durumlar var. Örneğin, bir işletim sistemi olmadan kesintileri değerlendirmek zorundasınız. Bir kesinti çok sık meydana gelirse ve ISR'de uzun bir değerlendirme yapmak için zamanınız yoksa, ISR'de sadece bazı küresel bayraklar belirlemek için sadece izin verilmez, bazen en iyi uygulama bile olabilir (mümkün olduğunca az zaman harcamanız gerekir) ISR) ve bu bayrakları ana döngünüzde değerlendirmek.


0

Programlamada hiçbir şeyin mutlak bir kötülük olduğunu sanmıyorum .

Bayrakların sıralı olabileceği başka bir durum daha var, burada henüz bahsedilmedi ...

Bu Javascript snippet'inde kapakların kullanımını göz önünde bulundurun:

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

"Array.forEach" öğesine iletilen iç işlev, basitçe "doğru döndürülemez".

Bu nedenle, devleti bir bayrakla birlikte dışarıda tutmanız gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.