Apakah salah menggunakan parameter boolean untuk menentukan perilaku?

194

Saya telah melihat latihan dari waktu ke waktu yang "terasa" salah, tetapi saya tidak bisa mengartikulasikan apa yang salah tentang itu. Atau mungkin itu hanya prasangka saya. Ini dia:

Pengembang mendefinisikan metode dengan boolean sebagai salah satu parameternya, dan metode itu memanggil yang lain, dan seterusnya, dan akhirnya boolean itu digunakan, semata-mata untuk menentukan apakah akan mengambil tindakan tertentu atau tidak. Ini dapat digunakan, misalnya, untuk memungkinkan tindakan hanya jika pengguna memiliki hak tertentu, atau mungkin jika kita (atau tidak) dalam mode uji atau mode batch atau mode hidup, atau mungkin hanya ketika sistem berada dalam negara tertentu.

Yah selalu ada cara lain untuk melakukannya, apakah dengan menanyakan kapan waktu untuk mengambil tindakan (daripada melewati parameter), atau dengan memiliki beberapa versi metode, atau beberapa implementasi dari kelas, dll. Pertanyaan saya bukan Begitu banyak cara untuk meningkatkan ini, tetapi lebih tepatnya apakah itu benar-benar salah (seperti yang saya duga), dan jika ya, apa yang salah tentang hal itu.

sinar
sumber
1
Ini adalah pertanyaan di mana keputusan berada. Pindahkan keputusan di tempat yang sentral alih-alih diserukan. Ini akan menjaga kompleksitas lebih rendah daripada memiliki faktor dua jalur kode setiap kali Anda memiliki if.
28
Martin Fowler memiliki artikel tentang ini: martinfowler.com/bliki/FlagArgument.html
Christoffer Hammarström
@ ChristofferHammarström Tautan yang bagus. Saya akan memasukkan itu dalam jawaban saya karena menjelaskan banyak detail ide yang sama dari penjelasan saya.
Alex
1
Saya tidak selalu setuju dengan apa yang dikatakan Nick, tetapi dalam kasus ini saya setuju 100%: Jangan gunakan parameter boolean .
Marjan Venema

Jawaban:

107

Ya, ini sepertinya bau kode, yang akan menyebabkan kode yang tidak dapat dipelihara yang sulit dipahami dan yang memiliki peluang lebih rendah untuk mudah digunakan kembali.

Seperti yang telah dicatat oleh poster, konteksnya adalah segalanya (jangan menyerah begitu saja jika itu salah satu atau jika praktiknya telah diakui sebagai utang teknis yang sengaja dikeluarkan untuk diperhitungkan kembali nanti) tetapi secara umum jika ada parameter yang lulus ke dalam fungsi yang memilih perilaku spesifik untuk dieksekusi maka penyempurnaan langkah-bijaksana lebih lanjut diperlukan; Memecah fungsi ini menjadi fungsi yang lebih kecil akan menghasilkan yang lebih kohesif.

Jadi apa fungsi yang sangat kohesif?

Ini adalah fungsi yang melakukan satu hal dan satu hal saja.

Masalah dengan parameter yang diteruskan seperti yang Anda gambarkan, adalah bahwa fungsinya melakukan lebih dari dua hal; mungkin atau mungkin tidak memeriksa hak akses pengguna tergantung pada status parameter Boolean, lalu tergantung pada pohon keputusan itu, ia akan melakukan fungsi.

Akan lebih baik untuk memisahkan masalah Kontrol Akses dari masalah Tugas, Tindakan atau Perintah.

Seperti yang telah Anda catat, jalinan masalah ini tampaknya tidak ada.

Jadi gagasan keterpaduan membantu kita mengidentifikasi bahwa fungsi yang dimaksud tidak sangat kohesif dan bahwa kita bisa memperbaiki kode untuk menghasilkan satu set fungsi yang lebih kohesif.

Jadi pertanyaannya bisa diajukan kembali; Mengingat bahwa kita semua sepakat untuk melewati parameter pemilihan perilaku sebaiknya dihindari bagaimana kita memperbaiki masalah?

Saya akan menyingkirkan parameter sepenuhnya. Memiliki kemampuan untuk mematikan kontrol akses bahkan untuk pengujian adalah risiko keamanan potensial. Untuk tujuan pengujian, atau cek akses untuk menguji baik akses yang diizinkan maupun skenario akses yang ditolak.

Ref: Kohesi (ilmu komputer)

rampok
sumber
Rob, maukah Anda memberikan penjelasan tentang apa Kohesi itu dan bagaimana itu berlaku?
Ray
Ray, semakin saya berpikir tentang ini semakin saya berpikir bahwa Anda harus keberatan dengan kode berdasarkan lubang keamanan boolean untuk mengaktifkan kontrol akses memperkenalkan ke dalam aplikasi. Peningkatan kualitas pada basis kode akan menjadi efek samping yang bagus;)
Rob
1
Penjelasan kohesi yang sangat bagus dan aplikasinya. Ini benar-benar harus mendapatkan lebih banyak suara. Dan saya setuju dengan masalah keamanan juga ... meskipun jika mereka semua metode pribadi itu adalah potensi kerentanan yang lebih kecil
Ray
1
Terima kasih, Ray. Kedengarannya seperti itu akan cukup mudah untuk faktor ulang ketika waktu memungkinkan. Mungkin patut memberikan komentar TODO untuk menyoroti masalah ini, menyeimbangkan antara otoritas teknis dan sensitivitas terhadap tekanan yang kadang-kadang kita alami untuk menyelesaikan sesuatu.
Rob
"unmaintainable"
aaa90210
149

Saya berhenti menggunakan pola ini sejak lama, karena alasan yang sangat sederhana; biaya perawatan. Beberapa kali saya menemukan bahwa saya memiliki beberapa fungsi mengatakan frobnicate(something, forwards_flag)yang dipanggil berkali-kali dalam kode saya, dan perlu menemukan semua tempat dalam kode di mana nilai falsedilewatkan sebagai nilai forwards_flag. Anda tidak dapat dengan mudah mencari itu, jadi ini menjadi sakit kepala pemeliharaan. Dan jika Anda perlu membuat perbaikan bug di masing-masing situs tersebut, Anda mungkin memiliki masalah yang tidak menguntungkan jika Anda melewatkannya.

Tetapi masalah khusus ini mudah diperbaiki tanpa mengubah pendekatan secara mendasar:

enum FrobnicationDirection {
  FrobnicateForwards,
  FrobnicateBackwards;
};

void frobnicate(Object what, FrobnicationDirection direction);

Dengan kode ini, seseorang hanya perlu mencari contoh FrobnicateBackwards. Meskipun mungkin ada beberapa kode yang memberikan ini ke variabel sehingga Anda harus mengikuti beberapa utas kontrol, saya menemukan dalam praktiknya bahwa ini cukup langka sehingga alternatif ini berfungsi dengan baik.

Namun, ada masalah lain dengan mengibarkan bendera dengan cara ini, setidaknya secara prinsip. Ini adalah bahwa beberapa (hanya beberapa) sistem yang memiliki desain ini mungkin mengekspos terlalu banyak pengetahuan tentang detail implementasi dari bagian-bagian kode yang bersarang (yang menggunakan bendera) ke lapisan luar (yang perlu mengetahui nilai yang harus dilewati di bendera ini). Untuk menggunakan terminologi Larry Constantine, desain ini mungkin memiliki kopling yang terlalu kuat antara penyetel dan pengguna bendera boolean. Franky meskipun sulit untuk mengatakan dengan tingkat kepastian pada pertanyaan ini tanpa mengetahui lebih lanjut tentang basis kode.

Untuk membahas contoh spesifik yang Anda berikan, saya akan memiliki beberapa tingkat kepedulian di masing-masing tetapi terutama karena alasan risiko / kebenaran. Yaitu, jika sistem Anda perlu membagikan flag-flag yang menunjukkan status sistem apa, Anda mungkin menemukan bahwa Anda memiliki kode yang seharusnya mempertimbangkan hal ini tetapi tidak memeriksa parameternya (karena tidak diteruskan ke fungsi ini). Jadi, Anda memiliki bug karena seseorang dihilangkan untuk melewati parameter.

Perlu juga diakui bahwa indikator keadaan sistem yang perlu diteruskan ke hampir setiap fungsi pada dasarnya adalah variabel global. Banyak kelemahan dari variabel global akan berlaku. Saya pikir dalam banyak kasus adalah praktik yang lebih baik untuk merangkum pengetahuan tentang kondisi sistem (atau kredensial pengguna, atau identitas sistem) dalam objek yang bertanggung jawab untuk bertindak dengan benar berdasarkan data tersebut. Kemudian Anda membagikan referensi ke objek itu sebagai lawan dari data mentah. Konsep kunci di sini adalah enkapsulasi .

James Youngman
sumber
9
Contoh nyata yang benar-benar hebat serta wawasan tentang sifat dari apa yang kita hadapi dan bagaimana hal itu mempengaruhi kita.
Ray
33
+1. Saya menggunakan enum untuk ini sebanyak mungkin. Saya telah melihat fungsi di mana boolparameter tambahan telah ditambahkan kemudian, dan panggilan mulai terlihat seperti DoSomething( myObject, false, false, true, false ). Tidak mungkin untuk mencari tahu apa arti argumen boolean tambahan, sedangkan dengan nilai enum yang dinamai secara bermakna, mudah.
Graeme Perrow
17
Oh man, akhirnya contoh yang bagus tentang bagaimana cara FrobnicateBackwards. Sedang mencari ini selamanya.
Alex Pritchard
38

Ini tidak selalu salah tetapi dapat mewakili bau kode .

Skenario dasar yang harus dihindari terkait parameter boolean adalah:

public void foo(boolean flag) {
    doThis();
    if (flag)
        doThat();
}

Kemudian ketika menelepon Anda biasanya akan menelepon foo(false)dan foo(true)tergantung pada perilaku yang Anda inginkan.

Ini benar-benar masalah karena ini adalah kasus kohesi yang buruk. Anda membuat ketergantungan antara metode yang tidak benar-benar diperlukan.

Apa yang harus Anda lakukan dalam kasus ini adalah meninggalkan doThisdan doThatsebagai metode terpisah dan publik kemudian lakukan:

doThis();
doThat();

atau

doThis();

Dengan begitu Anda menyerahkan keputusan yang benar kepada pemanggil (persis seperti jika Anda melewati parameter boolean) tanpa membuat kopling.

Tentu saja tidak semua parameter boolean digunakan dengan cara yang begitu buruk, tetapi itu pasti bau kode dan Anda benar untuk merasa curiga jika Anda melihat itu banyak dalam kode sumber.

Ini hanyalah salah satu contoh cara mengatasi masalah ini berdasarkan contoh yang saya tulis. Ada kasus-kasus lain di mana pendekatan yang berbeda akan diperlukan.

Ada artikel bagus dari Martin Fowler yang menjelaskan lebih lanjut ide yang sama.

PS: jika metode fooalih-alih memanggil dua metode sederhana memiliki implementasi yang lebih kompleks maka yang harus Anda lakukan adalah menerapkan metode ekstraksi refactoring kecil sehingga kode yang dihasilkan terlihat mirip dengan implementasi fooyang saya tulis.

Alex
sumber
1
Terima kasih telah memanggil istilah "bau kode" - Saya tahu baunya tidak enak, tetapi tidak bisa mengetahui baunya. Contoh Anda cukup tepat dengan apa yang saya lihat.
Ray
24
Ada banyak situasi di mana if (flag) doThat()di dalamnya foo()sah. Mendorong keputusan tentang meminta doThat()setiap repetisi memaksa penelepon yang harus dihapus jika Anda kemudian menemukan beberapa metode, flagperilaku juga perlu menelepon doTheOther(). Saya lebih suka menempatkan dependensi antara metode di kelas yang sama daripada harus menjelajahi semua penelepon nanti.
Blrfl
1
@ Bllfl: Ya, saya pikir refactoring yang lebih mudah adalah menciptakan doOnedan doBothmetode (masing-masing untuk kasus yang salah dan benar) atau menggunakan tipe enum yang terpisah, seperti yang disarankan oleh James Youngman
hugomg
@missingno: Anda masih memiliki masalah yang sama untuk mendorong kode berlebihan ke penelepon untuk membuat doOne()atau mengambil doBoth()keputusan. Subrutin / fungsi / metode memiliki argumen sehingga perilakunya dapat bervariasi. Menggunakan enum untuk kondisi Boolean benar-benar terdengar seperti mengulangi diri sendiri jika nama argumen sudah menjelaskan apa yang dilakukannya.
Blrfl
3
Jika memanggil dua metode satu demi satu atau satu metode saja dapat dianggap berlebihan maka itu juga berlebihan bagaimana Anda menulis blok try-catch atau mungkin juga jika lain. Apakah itu berarti Anda akan menulis fungsi untuk abstrak semuanya? Tidak! Hati-hati, membuat satu metode yang semua itu memanggil dua metode lain tidak selalu mewakili abstraksi yang baik.
Alex
29

Pertama: pemrograman bukanlah ilmu, tetapi seni. Jadi jarang ada cara "salah" dan "benar" untuk memprogram. Kebanyakan standar pengkodean hanyalah "preferensi" yang oleh beberapa programmer dianggap berguna; tetapi pada akhirnya mereka agak sewenang-wenang. Jadi saya tidak akan pernah label pilihan parameter menjadi "salah" dalam dan dari dirinya sendiri - dan tentu saja bukan sesuatu yang generik dan bermanfaat sebagai parameter boolean. Penggunaan a boolean(atau a int, dalam hal ini) untuk merangkum keadaan dibenarkan dalam banyak kasus.

Keputusan pengkodean, pada umumnya, harus didasarkan terutama pada kinerja dan pemeliharaan. Jika kinerja tidak dipertaruhkan (dan saya tidak bisa membayangkan bagaimana hal itu bisa terjadi dalam contoh Anda), maka pertimbangan Anda selanjutnya adalah: seberapa mudah ini bagi saya (atau redaktur masa depan) untuk dipertahankan? Apakah ini intuitif dan dapat dimengerti? Apakah ini terisolasi? Misalnya Anda panggilan fungsi dirantai sebenarnya tidak tampak berpotensi rapuh dalam hal ini: jika Anda memutuskan untuk mengubah Anda bIsUpuntuk bIsDown, berapa banyak tempat lain dalam kode akan perlu diubah juga? Juga, apakah daftar paramater Anda membengkak? Jika fungsi Anda memiliki 17 parameter, maka keterbacaan adalah masalah, dan Anda perlu mempertimbangkan kembali apakah Anda menghargai manfaat arsitektur berorientasi objek.

kmote
sumber
4
Saya menghargai peringatan di paragraf pertama. Saya sengaja provokatif dengan mengatakan "salah", dan tentu saja mengakui bahwa kita sedang berhadapan dengan "praktik terbaik" dan prinsip-prinsip desain, dan hal-hal semacam ini sering bersifat situasional, dan seseorang harus mempertimbangkan banyak faktor
Ray
13
Jawaban Anda mengingatkan saya pada kutipan yang saya tidak dapat mengingat sumber - "jika fungsi Anda memiliki 17 parameter, Anda mungkin kehilangan satu".
Joris Timmermans
Saya akan sangat setuju dengan yang satu ini, dan menerapkan pada pertanyaan untuk mengatakan bahwa ya itu sering merupakan ide yang buruk untuk dilewati dengan bendera boolean, tetapi tidak pernah sesederhana yang buruk / baik ...
JohnB
15

Saya pikir artikel kode Robert C Martins Clean menyatakan bahwa Anda harus menghilangkan argumen boolean di mana mungkin karena mereka menunjukkan metode melakukan lebih dari satu hal. Suatu metode harus melakukan satu hal dan satu hal saja yang saya pikir adalah salah satu motoya.

dreza
sumber
@dreza Anda mengacu pada Hukum Keriting .
MattDavey
Tentu saja dengan pengalaman Anda harus tahu kapan harus mengabaikan argumen tersebut.
gnasher729
8

Saya pikir yang paling penting di sini adalah menjadi praktis.

Ketika boolean menentukan seluruh perilaku, buat saja metode kedua.

Ketika boolean hanya menentukan sedikit perilaku di tengah, Anda mungkin ingin menyimpannya dalam satu untuk mengurangi duplikasi kode. Jika memungkinkan, Anda bahkan dapat membagi metode menjadi tiga: Dua metode panggilan untuk opsi boolean, dan metode yang melakukan sebagian besar pekerjaan.

Sebagai contoh:

private void FooInternal(bool flag)
{
  //do work
}

public void Foo1()
{
  FooInternal(true);
}

public void Foo2()
{
  FooInternal(false);
}

Tentu saja, dalam praktiknya Anda akan selalu memiliki titik di antara kedua ekstrem ini. Biasanya saya hanya pergi dengan apa yang terasa benar, tetapi saya lebih suka untuk berbuat kesalahan di samping duplikasi kode kurang.

Jorn
sumber
Saya hanya menggunakan argumen boolean untuk mengontrol perilaku dalam metode pribadi (seperti yang ditunjukkan di sini). Tapi masalahnya: Jika beberapa dufus memutuskan untuk meningkatkan visibilitas FooInternaldi masa depan, lalu apa?
ADTC
Sebenarnya, saya akan pergi ke rute lain. Kode di dalam FooInternal harus dibagi menjadi 4 bagian: 2 berfungsi untuk menangani kasus Boolean true / false, satu untuk pekerjaan yang terjadi sebelumnya, dan yang lain untuk pekerjaan yang terjadi setelahnya. Kemudian, Anda Foo1menjadi: { doWork(); HandleTrueCase(); doMoreWork() }. Idealnya, fungsi doWorkdan doMoreWorkmasing-masing dipecah menjadi (satu atau lebih) potongan tindakan yang bermakna (yaitu sebagai fungsi terpisah), bukan hanya dua fungsi demi pemisahan.
jpaugh
7

Saya suka pendekatan menyesuaikan perilaku melalui metode pembangun yang mengembalikan contoh tidak berubah. Begini cara JambuSplitter menggunakannya:

private static final Splitter MY_SPLITTER = Splitter.on(',')
       .trimResults()
       .omitEmptyStrings();

MY_SPLITTER.split("one,,,,  two,three");

Manfaat dari ini adalah:

  • Readibility unggul
  • Hapus pemisahan metode konfigurasi vs tindakan
  • Mempromosikan keterpaduan dengan memaksa Anda berpikir tentang apa objeknya, apa yang seharusnya dan tidak boleh dilakukan. Dalam hal ini a Splitter. Anda tidak akan pernah memasukkan someVaguelyStringRelatedOperation(List<Entity> myEntities)kelas yang disebut Splitter, tetapi Anda akan berpikir untuk menempatkannya sebagai metode statis di StringUtilskelas.
  • Mesin virtual tersebut sudah dikonfigurasikan terlebih dahulu oleh karena itu siap untuk diinjeksi dengan ketergantungan. Klien tidak perlu khawatir tentang apakah akan lulus trueatau falsemetode untuk mendapatkan perilaku yang benar.
oksayt
sumber
1
Saya sebagian dengan solusi Anda sebagai pecinta dan penginjil Guava besar ... tapi saya tidak bisa memberi Anda +1, karena Anda melewatkan bagian yang saya benar-benar cari, yang adalah apa yang salah (atau bau) tentang cara lain. Saya pikir itu sebenarnya tersirat dalam beberapa penjelasan Anda, jadi mungkin jika Anda bisa membuat itu eksplisit, itu akan menjawab pertanyaan dengan lebih baik.
Ray
Saya suka ide pemisahan metode konfigurasi dan acton!
Sher10ck
Tautan ke perpustakaan Guava rusak
Josh Noe
4

Pasti bau kode . Jika tidak melanggar Prinsip Tanggung Jawab Tunggal maka mungkin melanggar Tell, Don't Ask . Mempertimbangkan:

Jika ternyata tidak melanggar salah satu dari dua prinsip itu, Anda harus tetap menggunakan enum. Bendera Boolean adalah boolean yang setara dengan angka ajaib . foo(false)masuk akal sebanyak bar(42). Enum dapat berguna untuk Pola Strategi dan memiliki fleksibilitas untuk membiarkan Anda menambahkan strategi lain. (Ingatlah untuk memberi nama mereka dengan tepat .)

Contoh khusus Anda sangat mengganggu saya. Mengapa bendera ini melewati begitu banyak metode? Ini terdengar seperti Anda perlu membagi parameter Anda menjadi subclass .

Eva
sumber
4

TL; DR: jangan gunakan argumen boolean.

Lihat di bawah mengapa mereka buruk, dan bagaimana cara menggantinya (dalam huruf tebal).


Argumen Boolean sangat sulit dibaca, dan karenanya sulit dipertahankan. Masalah utama adalah bahwa tujuannya umumnya jelas ketika Anda membaca metode tanda tangan di mana argumen tersebut dinamai. Namun, penamaan parameter umumnya tidak diperlukan di sebagian besar bahasa. Jadi Anda akan memiliki anti-pola seperti di RSACryptoServiceProvider#encrypt(Byte[], Boolean)mana parameter boolean menentukan jenis enkripsi apa yang akan digunakan dalam fungsi tersebut.

Jadi, Anda akan menerima panggilan seperti:

rsaProvider.encrypt(data, true);

di mana pembaca harus mencari tanda tangan metode hanya untuk menentukan apa yang sebenarnya truebisa berarti. Melewati bilangan bulat tentu saja sama buruknya:

rsaProvider.encrypt(data, 1);

akan memberitahu Anda sama banyak - atau lebih tepatnya: sama sedikit. Bahkan jika Anda mendefinisikan konstanta yang digunakan untuk integer maka pengguna fungsi mungkin mengabaikannya dan tetap menggunakan nilai literal.

Cara terbaik untuk menyelesaikan ini adalah dengan menggunakan enumerasi . Jika Anda harus lulus enum RSAPaddingdengan dua nilai: OAEPatau PKCS1_V1_5Anda akan segera dapat membaca kode:

rsaProvider.encrypt(data, RSAPadding.OAEP);

Boolean hanya dapat memiliki dua nilai. Ini berarti bahwa jika Anda memiliki opsi ketiga, maka Anda harus memperbaiki tanda tangan Anda. Umumnya ini tidak dapat dengan mudah dilakukan jika kompatibilitas ke belakang merupakan masalah, jadi Anda harus memperluas kelas publik apa pun dengan metode publik lain. Inilah yang akhirnya dilakukan Microsoft ketika mereka memperkenalkan di RSACryptoServiceProvider#encrypt(Byte[], RSAEncryptionPadding)mana mereka menggunakan enumerasi (atau setidaknya kelas meniru enumerasi) alih-alih boolean.

Bahkan mungkin lebih mudah untuk menggunakan objek atau antarmuka penuh sebagai parameter, jika parameter itu sendiri perlu di-parameterkan. Dalam contoh di atas OAEP padding itu sendiri dapat diparameterisasi dengan nilai hash untuk digunakan secara internal. Perhatikan bahwa sekarang ada 6 algoritma hash SHA-2 dan 4 algoritma hash SHA-3, sehingga jumlah nilai enumerasi dapat meledak jika Anda hanya menggunakan enumerasi tunggal daripada parameter (ini mungkin hal berikutnya yang akan dicari tahu oleh Microsoft ).


Parameter Boolean juga dapat menunjukkan bahwa metode atau kelas tidak dirancang dengan baik. Seperti contoh di atas: pustaka kriptografis apa pun selain .NET tidak menggunakan flag padding dalam metode signature sama sekali.


Hampir semua guru perangkat lunak yang saya suka memperingatkan terhadap argumen boolean. Misalnya, Joshua Bloch memperingatkan mereka dalam buku yang sangat dihargai "Java Efektif". Secara umum mereka seharusnya tidak digunakan. Anda bisa berargumen bahwa mereka dapat digunakan jika ada satu parameter yang mudah dipahami. Tetapi meskipun demikian: Bit.set(boolean)mungkin lebih baik diimplementasikan menggunakan dua metode : Bit.set()dan Bit.unset().


Jika Anda tidak dapat langsung memperbaiki kode Anda dapat mendefinisikan konstanta untuk setidaknya membuatnya lebih mudah dibaca:

const boolean ENCRYPT = true;
const boolean DECRYPT = false;

...

cipher.init(key, ENCRYPT);

jauh lebih mudah dibaca daripada:

cipher.init(key, true);

bahkan jika Anda lebih suka:

cipher.initForEncryption(key);
cipher.initForDecryption(key);

sebagai gantinya.

Maarten Bodewes
sumber
3

Saya terkejut tidak ada yang menyebutkan parameter bernama .

Masalah yang saya lihat dengan boolean-flags adalah mereka merusak keterbacaan. Misalnya, apa yang ada truedi

myObject.UpdateTimestamps(true);

melakukan? Saya tidak punya ide. Tapi bagaimana dengan:

myObject.UpdateTimestamps(saveChanges: true);

Sekarang sudah jelas apa parameter yang kita lewati dimaksudkan untuk dilakukan: kita memberi tahu fungsi untuk menyimpan perubahannya. Dalam hal ini, jika kelasnya non-publik, saya pikir parameter boolean baik-baik saja.


Tentu saja, Anda tidak bisa memaksa pengguna kelas Anda untuk menggunakan parameter bernama. Untuk alasan ini, salah satu enumatau dua metode terpisah biasanya lebih disukai, tergantung pada seberapa umum kasus default Anda. Inilah yang dilakukan .Net:

//Enum used
double a = Math.Round(b, MidpointRounding.AwayFromZero);

//Two separate methods used
IEnumerable<Stuff> ascendingList = c.OrderBy(o => o.Key);
IEnumerable<Stuff> descendingList = c.OrderByDescending(o => o.Key); 
BlueRaja - Danny Pflughoeft
sumber
1
Pertanyaannya bukan tentang apa yang lebih disukai daripada perilaku menentukan bendera, itu apakah bendera seperti bau, dan jika demikian, mengapa
Ray
2
@ Ray: Saya tidak melihat perbedaan antara kedua pertanyaan itu. Dalam bahasa di mana Anda dapat menegakkan penggunaan parameter bernama, atau ketika Anda bisa memastikan parameter bernama akan selalu digunakan (mis. Metode pribadi) , parameter boolean baik-baik saja. Jika parameter bernama tidak dapat ditegakkan oleh bahasa (C #) dan kelas adalah bagian dari API publik, atau jika bahasa tidak mendukung parameter bernama (C ++), sehingga kode seperti myFunction(true)mungkin ditulis, itu adalah kode- bau.
BlueRaja - Danny Pflughoeft
Pendekatan parameter bernama bahkan lebih salah. Tanpa nama, orang akan dipaksa untuk membaca dokumen API. Dengan sebuah nama, Anda pikir Anda tidak perlu: tetapi paramter itu bisa saja salah paham. Misalnya, itu bisa digunakan untuk menyimpan (semua) perubahan, tetapi kemudian implementasinya berubah sedikit sehingga hanya menyimpan perubahan besar (untuk beberapa nilai besar).
Ingo
@Ingo saya tidak setuju. Itu masalah pemrograman generik; Anda dapat dengan mudah menentukan SaveBignama lain . Kode apa pun dapat dikacaukan, jenis kesalahan ini tidak khusus untuk parameter bernama.
Maarten Bodewes
1
@Ingo: Jika Anda dikelilingi oleh orang idiot, maka Anda pergi dan mencari pekerjaan di tempat lain. Hal semacam itu adalah untuk apa Anda memiliki ulasan kode.
gnasher729
1

Saya tidak bisa mengartikulasikan apa yang salah tentang itu.

Jika terlihat seperti bau kode, terasa seperti bau kode dan - yah - bau seperti bau kode, itu mungkin bau kode.

Yang ingin Anda lakukan adalah:

1) Hindari metode dengan efek samping.

2) Tangani negara-negara yang diperlukan dengan mesin negara resmi pusat (seperti ini ).

BaBu
sumber
1

Saya setuju dengan semua kekhawatiran menggunakan Parameter Boolean untuk tidak menentukan kinerja agar; meningkatkan, Keterbacaan, Kehandalan, menurunkan Kompleksitas, Menurunkan risiko dari Enkapsulasi & Kohesi yang buruk dan menurunkan Total Biaya Kepemilikan dengan Dapat Diurus.

Saya mulai merancang perangkat keras pada pertengahan 70-an yang sekarang kita sebut SCADA (kontrol pengawasan dan akuisisi data) dan ini adalah perangkat keras yang disesuaikan dengan kode mesin di EPROM yang menjalankan kontrol jarak jauh makro dan mengumpulkan data kecepatan tinggi.

Logika disebut mesin Mealey & Moore yang kita sebut sekarang Mesin Hingga Negara . Ini juga harus dilakukan dalam aturan yang sama seperti di atas, kecuali jika itu adalah mesin waktu nyata dengan waktu eksekusi yang terbatas dan kemudian pintasan harus dilakukan untuk memenuhi tujuan tersebut.

Data tersebut sinkron tetapi perintahnya asinkron dan logika perintah mengikuti logika Boolean tanpa memori tetapi dengan perintah berurutan berdasarkan memori dari kondisi sebelumnya, sekarang, dan yang diinginkan berikutnya. Agar hal itu berfungsi dalam bahasa mesin yang paling efisien (hanya 64 kB), kami sangat berhati-hati untuk mendefinisikan setiap proses dengan gaya IBM HIPO heuristik. Itu kadang-kadang berarti melewati variabel Boolean dan melakukan cabang yang diindeks.

Tetapi sekarang dengan banyak memori dan kemudahan OOK, Enkapsulasi adalah unsur penting hari ini tetapi hukuman ketika kode dihitung dalam byte untuk waktu nyata dan kode mesin SCADA ..

Tony Stewart Sunnyskyguy EE75
sumber
0

Ini tidak selalu salah, tetapi dalam contoh konkret Anda tentang tindakan tergantung pada beberapa atribut "pengguna" saya akan melewati referensi ke pengguna daripada bendera.

Ini mengklarifikasi dan membantu dalam beberapa cara.

Siapa pun yang membaca pernyataan memohon akan menyadari bahwa hasilnya akan berubah tergantung pada pengguna.

Dalam fungsi yang pada akhirnya disebut, Anda dapat dengan mudah menerapkan aturan bisnis yang lebih kompleks karena Anda dapat mengakses atribut pengguna.

Jika satu fungsi / metode dalam "rantai" melakukan sesuatu yang berbeda tergantung pada atribut pengguna, sangat mungkin bahwa ketergantungan yang sama pada atribut pengguna akan diperkenalkan ke beberapa metode lain dalam "rantai".

James Anderson
sumber
0

Sebagian besar waktu, saya akan mempertimbangkan pengkodean yang buruk ini. Saya bisa memikirkan dua kasus, di mana ini mungkin merupakan praktik yang baik. Karena sudah ada banyak jawaban yang mengatakan mengapa itu buruk, saya menawarkan dua kali padahal mungkin bagus:

Yang pertama adalah jika setiap panggilan dalam urutan masuk akal dengan sendirinya. Masuk akal jika kode panggilan mungkin diubah dari true ke false atau false ke true, atau jika metode yang dipanggil mungkin diubah untuk menggunakan parameter boolean secara langsung daripada meneruskannya. Kemungkinan sepuluh panggilan seperti itu berturut-turut kecil, tetapi itu bisa terjadi, dan jika itu dilakukan, itu akan menjadi praktik pemrograman yang baik.

Kasus kedua adalah sedikit peregangan mengingat bahwa kita sedang berhadapan dengan boolean. Tetapi jika program memiliki banyak utas atau peristiwa, melewati parameter adalah cara paling sederhana untuk melacak utas / data khusus acara. Misalnya, suatu program dapat memperoleh input dari dua soket atau lebih. Kode yang berjalan untuk satu soket mungkin perlu menghasilkan pesan peringatan, dan satu yang berjalan untuk yang lain mungkin tidak. Maka (semacam) masuk akal untuk set boolean pada tingkat yang sangat tinggi untuk melewati banyak panggilan metode ke tempat-tempat di mana pesan peringatan dapat dihasilkan. Data tidak dapat disimpan (kecuali dengan susah payah) dalam bentuk global apa pun karena beberapa utas atau acara yang saling terkait masing-masing akan membutuhkan nilainya sendiri.

Yang pasti, dalam kasus terakhir saya mungkin membuat kelas / struktur yang hanya konten adalah boolean dan lulus yang sekitar sebagai gantinya. Saya hampir pasti akan membutuhkan bidang lain sebelum terlalu lama - seperti di mana mengirim pesan peringatan, misalnya.

RalphChapin
sumber
Sebuah PERINGATAN enum, SILENT akan lebih masuk akal, bahkan ketika menggunakan kelas / struktur seperti yang Anda tulis (yaitu konteks ). Atau memang hanya mengkonfigurasi logging secara eksternal - tidak perlu melewati apa pun.
Maarten Bodewes
-1

Konteks itu penting. Metode semacam itu sangat umum di iOS. Sebagai salah satu contoh yang sering digunakan, UINavigationController menyediakan metode -pushViewController:animated:, dan animatedparameternya adalah BOOL. Metode ini pada dasarnya menjalankan fungsi yang sama, tetapi menghidupkan transisi dari satu pengontrol tampilan ke yang lain jika Anda memasukkan YA, dan tidak jika Anda melewatkan TIDAK. Ini tampaknya sepenuhnya masuk akal; itu konyol untuk harus menyediakan dua metode di tempat ini hanya supaya Anda dapat menentukan apakah akan menggunakan animasi atau tidak.

Mungkin lebih mudah untuk membenarkan hal semacam ini di Objective-C, di mana sintaks penamaan metode memberikan lebih banyak konteks untuk setiap parameter daripada yang Anda dapatkan dalam bahasa seperti C dan Java. Namun demikian, saya akan berpikir bahwa metode yang menggunakan parameter tunggal dapat dengan mudah mengambil boolean dan masih masuk akal:

file.saveWithEncryption(false);    // is there any doubt about the meaning of 'false' here?
Caleb
sumber
21
Sebenarnya saya tidak tahu apa falseartinya dalam file.saveWithEncryptioncontoh ini. Apakah itu berarti akan menghemat tanpa enkripsi? Jika demikian, mengapa metode ini memiliki "dengan enkripsi" tepat di namanya? Saya bisa mengerti memiliki metode seperti save(boolean withEncryption), tetapi ketika saya melihat file.save(false), sama sekali tidak jelas bahwa parameter menunjukkan bahwa itu akan dengan atau tanpa enkripsi. Saya pikir, pada kenyataannya, ini membuat poin James Youngman pertama, tentang menggunakan enum.
Ray
6
Hipotesis alternatif adalah itu falseberarti, jangan terlalu mahal file yang ada dengan nama yang sama. Contoh buatan saya tahu, tetapi untuk memastikan Anda perlu memeriksa dokumentasi (atau kode) untuk fungsinya.
James Youngman
5
Metode bernama saveWithEncryptionyang terkadang tidak disimpan dengan enkripsi adalah bug. Seharusnya mungkin file.encrypt().save(), atau seperti orang Jawa new EncryptingOutputStream(new FileOutputStream(...)).write(file).
Christoffer Hammarström
2
Sebenarnya, kode yang melakukan sesuatu yang lain dari yang dikatakannya tidak berfungsi, dan karenanya merupakan bug. Itu tidak terbang untuk membuat metode saveWithEncryption(boolean)yang kadang-kadang menyimpan tanpa enkripsi, sama seperti itu tidak terbang untuk membuat saveWithoutEncryption(boolean)yang kadang-kadang menyimpan dengan enkripsi.
Christoffer Hammarström
2
Metodenya buruk, karena jelas ada "keraguan tentang arti 'salah' di sini". Lagi pula, saya tidak akan pernah menulis metode seperti itu di tempat pertama. Menyimpan dan mengenkripsi adalah tindakan terpisah, dan metode harus melakukan satu hal dan melakukannya dengan baik. Lihat komentar saya sebelumnya untuk contoh yang lebih baik bagaimana melakukannya.
Christoffer Hammarström