Apakah variabel bendera adalah kejahatan absolut? [Tutup]

47

Apakah variabel bendera jahat? Apakah jenis variabel berikut ini sangat tidak bermoral dan apakah jahat menggunakannya?

"variabel boolean atau integer yang Anda tetapkan nilai di tempat-tempat tertentu kemudian di bawah Anda periksa lalu orther untuk melakukan sesuatu atau tidak, seperti, misalnya menggunakan newItem = truebeberapa baris di bawah ini if (newItem ) then"


Saya ingat melakukan beberapa proyek di mana saya benar-benar diabaikan menggunakan bendera dan berakhir dengan arsitektur / kode yang lebih baik; Namun, ini adalah praktik umum di proyek lain tempat saya bekerja, dan ketika kode tumbuh dan bendera ditambahkan, kode-spaghetti IMHO juga tumbuh.

Apakah Anda mengatakan ada kasus di mana menggunakan bendera adalah praktik yang baik atau bahkan perlu ?, atau apakah Anda setuju bahwa menggunakan bendera dalam kode adalah ... bendera merah dan harus dihindari / refactored; saya, saya hanya bertahan dengan melakukan fungsi / metode yang memeriksa keadaan secara real time.

dukeofgaming
sumber
9
Sepertinya MainMa dan saya memiliki definisi "bendera" yang berbeda. Saya sedang memikirkan preprocessor #ifdefs. Yang mana yang kamu tanyakan?
Karl Bielefeldt
Ini pertanyaan yang sangat bagus. Saya sendiri sering bertanya-tanya, dan sebenarnya saya berkata "oh well, mari kita gunakan bendera" terlalu banyak.
Paul Richter
Boolean adalah bendera. (Begitu juga bilangan bulat, demikian juga ...)
Thomas Eding
7
@KarlBielefeldt Saya percaya OP merujuk ke variabel boolean atau bilangan bulat yang Anda tetapkan nilai di tempat-tempat tertentu lalu turun di bawah Anda centang lalu orther untuk melakukan sesuatu atau tidak, seperti, misalnya menggunakan newItem = truebeberapa baris di bawah iniif (newItem ) then
Tulains Córdova
1
Juga pertimbangkan pengantar menjelaskan variabel refactoring dalam konteks ini. Selama metode ini tetap pendek dan memiliki jumlah jalur yang rendah, saya menganggap ini sebagai penggunaan yang valid.
Daniel B

Jawaban:

41

Masalah yang saya lihat ketika mempertahankan kode yang menggunakan bendera adalah bahwa jumlah negara tumbuh dengan cepat, dan hampir selalu ada keadaan yang tidak ditangani. Satu contoh dari pengalaman saya sendiri: Saya sedang mengerjakan beberapa kode yang memiliki ketiga flag ini

bool capturing, processing, sending;

Ketiga negara ini menciptakan delapan negara bagian (sebenarnya, ada dua bendera lainnya juga). Tidak semua kombinasi nilai yang mungkin dicakup oleh kode, dan pengguna melihat bug:

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

Ternyata ada situasi di mana asumsi dalam pernyataan if di atas salah.

Bendera cenderung bertambah dari waktu ke waktu, dan mereka menyembunyikan keadaan kelas yang sebenarnya. Itu sebabnya mereka harus dihindari.

Ben
sumber
3
+1, "harus dihindari". Saya akan menambahkan sesuatu tentang 'tetapi bendera diperlukan dalam beberapa situasi' (beberapa mungkin mengatakan 'kejahatan yang diperlukan')
Trevor Boyd Smith
2
@TrevorBoydSmith Dalam pengalaman saya tidak, Anda hanya memerlukan sedikit lebih banyak dari kekuatan otak rata-rata yang akan Anda gunakan untuk sebuah bendera
dukeofgaming
Dalam ujian Anda itu harus menjadi satu negara mewakili enum, bukan 3 boolean.
user949300
Anda mungkin mengalami masalah serupa dengan apa yang saya hadapi saat ini. Selain mencakup semua status yang mungkin, dua aplikasi mungkin berbagi bendera yang sama (misalnya mengunggah data pelanggan). Dalam hal ini, hanya satu Pengunggah akan menggunakan bendera, mematikannya dan semoga berhasil menemukan masalah di masa depan.
Alan
38

Berikut ini contoh ketika bendera berguna.

Saya memiliki sepotong kode yang menghasilkan kata sandi (menggunakan generator nomor pseudorandom yang aman secara kriptografis). Penelepon metode memilih apakah kata sandi harus mengandung huruf kapital, huruf kecil, digit, simbol dasar, simbol diperluas, simbol Yunani, Cyrillic dan unicode.

Dengan tanda, memanggil metode ini mudah:

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

dan bahkan dapat disederhanakan untuk:

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

Tanpa bendera, apa yang akan menjadi metode tanda tangan?

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

disebut seperti ini:

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

Seperti disebutkan dalam komentar, pendekatan lain adalah menggunakan koleksi:

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

Ini jauh lebih mudah dibaca dibandingkan dengan himpunan truedan false, tetapi masih memiliki dua kelemahan:

Kelemahan utama adalah bahwa untuk memungkinkan nilai gabungan, seperti CharacterSet.LettersAndDigitsAnda akan menulis sesuatu seperti itu dalam Generate()metode:

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

mungkin ditulis ulang seperti ini:

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

Bandingkan ini dengan yang Anda miliki dengan menggunakan flag:

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

Kelemahan kedua yang sangat kecil adalah bahwa tidak jelas bagaimana metode akan berperilaku jika dipanggil seperti ini:

var password = this.PasswordGenerator.Generate(
    new []
    {
        CharacterSet.Digits,
        CharacterSet.LettersAndDigits, // So digits are requested two times.
    });
Arseni Mourzenko
sumber
10
Saya percaya OP mengacu pada variabel boolean atau bilangan bulat yang Anda tetapkan nilai di tempat-tempat tertentu kemudian di bawah Anda periksa lalu orther untuk melakukan sesuatu atau tidak, seperti, misalnya menggunakan newItem = truebeberapa baris di bawah iniif (newItem ) then
Tulains Córdova
1
@MainMa Rupanya ada yang ke-3: Versi dengan 8 argumen boolean adalah apa yang saya pikirkan ketika saya membaca "bendera" ...
Izkata
4
Maaf, tapi IMHO ini adalah kasus yang sempurna untuk metode chaining ( en.wikipedia.org/wiki/Method_chaining ), Selain itu, Anda dapat menggunakan array parameter (harus berupa array asosiatif atau peta), di mana setiap entri dalam array parameter itu Anda menghilangkan menggunakan perilaku nilai default untuk parameter itu. Pada akhirnya, panggilan melalui metode chaining atau parameter array dapat secara ringkas dan ekspresif seperti flag bit, juga, tidak setiap bahasa memiliki operator bit (saya benar-benar menyukai flag biner, tetapi akan menggunakan metode yang baru saja saya sebutkan sebagai gantinya).
dukeofgaming
3
Itu tidak terlalu OOP, kan? Saya akan membuat antarmuka ala: String myNewPassword = makePassword (randomComposeSupplier (RandomLowerCaseSupplier baru), RandomUpperCaseSupplier baru (), RandomNumberSupplier baru),; dengan String makePassword (Pemasok <Character> charSupplier); dan Pemasok <Character> randomComposeSupplier (Pemasok <Character> ... pemasok); Sekarang Anda dapat menggunakan kembali pemasok Anda untuk tugas-tugas lain, menyusun mereka dengan cara apa pun yang Anda suka dan menyederhanakan metode generatePassword Anda sehingga menggunakan kondisi minimal.
Dibbeke
4
@Dibbeke Bicara tentang kerajaan kata benda ...
Phil
15

Blok fungsi besar adalah baunya, bukan bendera. Jika Anda mengatur bendera pada baris 5, maka hanya memeriksa bendera pada baris 354, maka itu buruk. Jika Anda mengatur bendera pada baris 8 dan memeriksa bendera pada baris 10, itu tidak masalah. Juga, satu atau dua flag per blok kode baik-baik saja, 300 flag dalam suatu fungsi buruk.

nbv4
sumber
10

Biasanya bendera dapat sepenuhnya diganti oleh beberapa rasa dari pola strategi, dengan satu implementasi strategi untuk setiap nilai bendera yang mungkin. Ini membuat menambahkan perilaku baru jauh lebih mudah.

Dalam situasi kritis kinerja, biaya tipuan mungkin muncul dan membuat dekonstruksi menjadi bendera yang jelas diperlukan. Yang sedang berkata saya mengalami kesulitan untuk mengingat satu kasus di mana saya benar-benar harus melakukan itu.

back2dos
sumber
6

Tidak, bendera tidak buruk atau jahat yang harus di refactored dengan cara apa pun.

Pertimbangkan panggilan Pattern.compile Java (String regex, int flags) . Ini adalah bitmask tradisional dan berfungsi. Sekilas konstanta di Jawa dan di mana pun Anda melihat sekelompok 2 n Anda tahu ada bendera di sana.

Dalam dunia refactored yang ideal, seseorang malah akan menggunakan EnumSet di mana konstanta bukan nilai dalam enum dan ketika dokumentasi berbunyi:

Kinerja ruang dan waktu dari kelas ini harus cukup baik untuk memungkinkan penggunaannya sebagai alternatif berkualitas tinggi dan aman untuk "bendera bit" berbasis int tradisional.

Di dunia yang sempurna, panggilan Pattern.compile itu menjadi Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags).

Semua yang dikatakan, masih bendera. Jauh lebih mudah untuk dikerjakan Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)daripada memiliki Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline())atau gaya lain dalam mencoba melakukan apa sebenarnya bendera dan manfaatnya.

Bendera sering terlihat ketika bekerja dengan hal-hal tingkat sistem. Ketika berinteraksi dengan sesuatu di tingkat sistem operasi, orang cenderung memiliki bendera di suatu tempat - baik itu nilai pengembalian suatu proses, atau izin file, atau bendera untuk membuka soket. Mencoba untuk memperbaiki kejadian ini dalam beberapa perburuan terhadap bau kode yang dirasakan kemungkinan akan berakhir dengan kode yang lebih buruk daripada jika orang yang digunakan menerima dan memahami benderanya.

Masalahnya terjadi ketika orang menyalahgunakan bendera untuk melempar mereka bersama-sama dan membuat set frankenflag dari semua jenis bendera yang tidak terkait atau mencoba menggunakannya di mana mereka bukan bendera sama sekali.


sumber
5

Saya berasumsi kita berbicara tentang flag dalam tanda tangan metode.

Menggunakan satu bendera saja sudah cukup buruk.

Ini tidak akan berarti apa-apa bagi kolega Anda saat mereka melihatnya. Mereka harus melihat kode sumber metode untuk menetapkan apa yang dilakukannya. Anda mungkin akan berada di posisi yang sama beberapa bulan ke depan, ketika Anda lupa tentang apa metode Anda.

Melewati bendera ke metode, biasanya berarti bahwa metode Anda bertanggung jawab untuk banyak hal. Di dalam metode Anda mungkin melakukan pemeriksaan sederhana pada baris:

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

Itu adalah pemisahan keprihatinan yang buruk dan Anda biasanya dapat menemukan jalan keluarnya.

Saya biasanya memiliki dua metode terpisah:

public void DoFlagSet() 
{
}

public void DoFlagNotSet()
{
}

Ini akan lebih masuk akal dengan nama metode yang berlaku untuk masalah yang Anda pecahkan.

Melewati banyak bendera dua kali lebih buruk. Jika Anda benar-benar harus melewati beberapa flag, daripada mempertimbangkan untuk merangkumnya dalam sebuah kelas. Meski begitu, Anda masih akan menghadapi masalah yang sama, karena metode Anda kemungkinan melakukan banyak hal.

CodeART
sumber
3

Bendera dan sebagian besar variabel temp adalah bau yang kuat. Kemungkinan besar mereka bisa di refactored dan diganti dengan metode kueri.

Direvisi:

Flag dan variabel temp ketika menyatakan status, harus direaktor ulang ke metode kueri. Nilai-nilai negara (boolean, int, dan primitif lainnya) harus hampir selalu disembunyikan sebagai bagian dari detail implementasi.

Bendera yang digunakan untuk kontrol, perutean, dan aliran program umum juga dapat menunjukkan peluang untuk merombak bagian-bagian dari struktur kontrol menjadi strategi atau pabrik yang terpisah, atau apa pun yang mungkin sesuai secara situasional, yang terus menggunakan metode kueri.

JustinC
sumber
2

Ketika kita berbicara tentang bendera, kita harus tahu bahwa mereka akan dimodifikasi dari waktu ke waktu pelaksanaan program dan bahwa mereka akan mempengaruhi perilaku program berdasarkan negara mereka. Selama kita memiliki kontrol rapi atas dua hal ini, mereka akan bekerja dengan baik.

Bendera bisa berfungsi dengan baik jika

  • Anda telah mendefinisikannya dalam cakupan yang sesuai. Maksud saya, ruang lingkup tidak boleh berisi kode apa pun yang tidak perlu / tidak boleh dimodifikasi. Atau setidaknya kodenya aman (misalnya tidak dapat dipanggil langsung dari luar)
  • Jika ada kebutuhan untuk menangani flag dari luar dan jika ada banyak flag, kita dapat mengkode flag handler sebagai satu-satunya cara untuk memodifikasi flag dengan aman. Handler flag ini dapat merangkum flag dan metode untuk memodifikasinya. Itu dapat kemudian dibuat singleton dan kemudian dapat dibagikan di antara kelas-kelas yang membutuhkan akses ke bendera.
  • Dan akhirnya untuk pemeliharaan, jika ada terlalu banyak bendera:
    • Tidak perlu mengatakan mereka harus mengikuti penamaan yang masuk akal
    • Harus didokumentasikan dengan nilai yang valid (mungkin dengan enumerasi)
    • Harus didokumentasikan dengan WHICH CODE AKAN MEMODIFIKASI masing-masing, dan juga dengan KONDISI WHICH yang akan menghasilkan penugasan nilai tertentu ke bendera.
    • KODE YANG AKAN MENGKONSUMSI mereka dan PERILAKU APA yang akan menghasilkan nilai tertentu

Jika ada banyak flag, pekerjaan desain yang baik harus didahului sejak flag kemudian mulai memainkan peran kunci dalam perilaku program. Anda dapat memilih diagram Negara untuk pemodelan. Diagram seperti itu juga berfungsi sebagai dokumentasi dan panduan visual saat menghadapinya.

Selama semua ini ada di tempat saya pikir itu tidak akan menyebabkan kekacauan.

Mahesha999
sumber
1

Saya berasumsi dari pertanyaan bahwa QA adalah variabel flag makna (global), dan bukan bit parameter fungsi.

Ada situasi di mana Anda tidak memiliki banyak kemungkinan lain. Misalnya, tanpa sistem operasi Anda harus mengevaluasi interupsi. Jika interupsi datang sangat sering dan Anda tidak punya waktu untuk melakukan evaluasi panjang dalam ISR, itu tidak hanya diizinkan tetapi kadang-kadang bahkan praktik terbaik untuk hanya menetapkan beberapa bendera global di ISR ​​(Anda harus menghabiskan waktu sesedikit mungkin. di ISR), dan untuk mengevaluasi flag-flag itu di loop utama Anda.

vsz
sumber
0

Saya tidak pernah berpikir bahwa apapun adalah kejahatan mutlak dalam pemrograman.

Ada situasi lain di mana bendera mungkin dalam urutan, yang belum disebutkan di sini ...

Pertimbangkan penggunaan penutupan dalam cuplikan Javascript ini:

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

Fungsi batin, diteruskan ke "Array.forEach", tidak bisa begitu saja "mengembalikan true".

Karenanya, Anda perlu menjaga negara di luar dengan bendera.

firstdoit
sumber