Saya memiliki sepotong kode di mana saya mengulangi peta sampai kondisi tertentu benar dan kemudian menggunakan kondisi itu untuk melakukan beberapa hal lagi.
Contoh:
Map<BigInteger, List<String>> map = handler.getMap();
if(map != null && !map.isEmpty())
{
for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
{
fillUpList();
if(list.size() > limit)
{
limitFlag = true;
break;
}
}
}
else
{
logger.info("\n>>>>> \n\t 6.1 NO entries to iterate over (for given FC and target) \n");
}
if(!limitFlag) // Continue only if limitFlag is not set
{
// Do something
}
Saya merasa menetapkan bendera dan kemudian menggunakannya untuk melakukan lebih banyak hal adalah bau kode.
Apakah saya benar? Bagaimana saya bisa menghapus ini?
design-patterns
object-oriented
object-oriented-design
clean-code
Siddharth Trikha
sumber
sumber
entry
tidak digunakan di dalam loop fungsi, dan kita hanya bisa menebak apalist
itu. ApakahfillUpList
seharusnya diisilist
? Mengapa tidak mendapatkannya sebagai parameter?Jawaban:
Tidak ada yang salah dengan menggunakan nilai Boolean untuk tujuan yang dimaksudkan: untuk merekam perbedaan biner.
Jika saya disuruh refactor kode ini, saya mungkin akan memasukkan loop ke dalam metode sendiri sehingga + tugas
break
menjadi +return
; maka Anda bahkan tidak perlu variabel, Anda bisa mengatakannyasumber
fillUpList()
ada beberapa kode (yang OP memutuskan untuk tidak membagikannya) yang benar-benar menggunakan nilaientry
dari iterasi; tanpa asumsi ini, itu akan terlihat seperti loop body tidak menggunakan apa pun dari iterasi loop.Itu tidak selalu buruk, dan kadang-kadang itu adalah solusi terbaik. Tetapi pengaturan bendera seperti ini di blok bersarang dapat membuat kode sulit untuk diikuti.
Masalahnya adalah Anda memiliki blok untuk membatasi cakupan, tetapi kemudian Anda memiliki bendera yang berkomunikasi lintas cakupan, melanggar isolasi logis dari blok. Sebagai contoh, itu
limitFlag
akan menjadi false jikamap
isnull
, jadi "do something" -code akan dieksekusi ifmap
isnull
. Ini mungkin yang Anda maksudkan, tetapi itu bisa berupa bug yang mudah terlewatkan, karena kondisi untuk bendera ini ditentukan di tempat lain, di dalam lingkup bersarang. Jika Anda dapat menyimpan informasi dan logika di dalam ruang lingkup seketat mungkin, Anda harus berusaha melakukannya.sumber
if(map==null || map.isEmpty()) { logger.info(); return;}
ini, akan tetapi, hanya bekerja jika kode yang kita lihat adalah seluruh fungsi, dan// Do something
bagian tersebut tidak diperlukan jika peta adalah nol atau kosong.Saya akan menyarankan agar tidak berpikir tentang 'kode bau'. Itu hanya cara paling malas untuk merasionalisasi bias Anda sendiri. Seiring waktu Anda akan mengembangkan banyak bias, dan banyak dari mereka akan masuk akal, tetapi banyak dari mereka akan menjadi bodoh.
Sebagai gantinya, Anda harus memiliki alasan praktis (yaitu, bukan dogmatis) untuk lebih memilih satu hal daripada yang lain, dan menghindari berpikir bahwa Anda harus memiliki jawaban yang sama untuk semua pertanyaan serupa.
"Kode berbau" adalah untuk saat Anda tidak berpikir. Jika Anda benar-benar akan memikirkan kode itu, maka lakukan dengan benar!
Dalam hal ini, keputusan dapat benar-benar berjalan baik tergantung pada kode di sekitarnya. Itu benar-benar tergantung pada apa yang Anda pikirkan adalah cara paling jelas untuk berpikir tentang apa yang dilakukan kode. (kode "bersih" adalah kode yang dengan jelas mengomunikasikan apa yang dilakukannya ke pengembang lain dan membuatnya mudah bagi mereka untuk memverifikasi bahwa itu benar)
Sering kali, orang akan menulis metode yang terstruktur menjadi beberapa fase, di mana kode pertama-tama akan menentukan apa yang perlu diketahui tentang data dan kemudian bertindak berdasarkan hal itu. Jika bagian "tentukan" dan bagian "lakari" keduanya sedikit rumit, maka masuk akal untuk melakukan ini, dan sering kali "apa yang perlu diketahui" dapat dilakukan di antara fase dalam bendera Boolean. Saya benar-benar lebih suka Anda memberi bendera nama yang lebih baik. Sesuatu seperti "largeEntryExists" akan membuat kode lebih bersih.
Di lain pihak, jika kode "// Do Something" sangat sederhana, maka lebih masuk akal untuk meletakkannya di dalam
if
blok alih-alih mengatur bendera. Itu menempatkan efek lebih dekat ke penyebabnya, dan pembaca tidak harus memindai sisa kode untuk memastikan bahwa flag mempertahankan nilai yang Anda tetapkan.sumber
Ya, ini adalah bau kode (isyarat turun dari semua orang yang melakukannya).
Kuncinya bagi saya adalah penggunaan
break
pernyataan itu. Jika Anda tidak menggunakannya maka Anda akan mengulangi item lebih dari yang dibutuhkan, tetapi menggunakannya memberikan dua kemungkinan titik keluar dari loop.Bukan masalah besar dengan contoh Anda, tetapi Anda dapat membayangkan bahwa sebagai conditional atau conditional di dalam loop menjadi lebih kompleks atau urutan daftar awal menjadi penting maka lebih mudah bagi bug untuk merangkak ke dalam kode.
Ketika kode sesederhana contoh Anda, dapat dikurangi menjadi satu
while
lingkaran atau peta yang setara, membangun filter.Ketika kode ini cukup kompleks untuk membutuhkan flag dan break maka akan rentan terhadap bug.
Seperti halnya semua kode berbau: Jika Anda melihat bendera, coba ganti dengan a
while
. Jika tidak bisa, tambahkan tes unit tambahan.sumber
SO as with all code smells: If you see a flag, try to replace it with a while
Bisakah Anda menguraikan ini dengan contoh?for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
denganfor (Iterator<Map.Entry<BigInteger, List<String>>> iterator = map.entrySet().iterator(); iterator.hasNext() && !limitFlag; Map.Entry<BigInteger, List<String>> entry = iterator.next())
. Itu adalah pola yang tidak biasa sehingga saya lebih sulit memahaminya daripada istirahat yang relatif sederhana.Cukup gunakan nama selain limitFlag yang memberitahu apa yang sebenarnya Anda periksa. Dan mengapa Anda mencatat apa pun saat peta tidak ada atau kosong? limtFlag akan salah, semua yang Anda pedulikan. Loopnya baik-baik saja jika peta kosong, jadi tidak perlu memeriksa itu.
sumber
Menetapkan nilai boolean untuk menyampaikan informasi yang sudah Anda miliki merupakan praktik buruk menurut saya. Jika tidak ada alternatif yang mudah maka itu mungkin menunjukkan masalah yang lebih besar seperti enkapsulasi yang buruk.
Anda harus memindahkan logika loop ke metode fillUpList agar dapat rusak jika batas tercapai. Kemudian periksa ukuran daftar secara langsung sesudahnya.
Jika itu merusak kode Anda, mengapa?
sumber
Pertama dari kasus umum: Menggunakan bendera untuk memeriksa apakah beberapa elemen koleksi memenuhi kondisi tertentu tidak biasa. Tetapi pola yang paling sering saya lihat untuk menyelesaikan ini adalah dengan memindahkan cek dalam metode tambahan dan langsung kembali darinya (seperti yang dijelaskan oleh Kilian Foth dalam jawabannya ):
Karena Java 8 ada cara yang lebih ringkas menggunakan
Stream.anyMatch(…)
:Dalam kasus Anda, ini mungkin akan terlihat seperti ini (dengan asumsi
list == entry.getValue()
dalam pertanyaan Anda):Masalah dalam contoh spesifik Anda adalah panggilan tambahan untuk
fillUpList()
. Jawabannya sangat tergantung pada apa yang seharusnya dilakukan metode ini.Catatan: Saat berdiri, panggilan untuk
fillUpList()
tidak masuk akal, karena tidak bergantung pada elemen yang saat ini Anda gunakan. Saya kira ini adalah konsekuensi dari melucuti kode Anda yang sebenarnya agar sesuai dengan format pertanyaan. Tapi justru itu mengarah pada contoh buatan yang sulit diartikan dan karenanya sulit dipikirkan. Oleh karena itu sangat penting untuk memberikan Minimal, Lengkap , dan diverifikasi contoh .Jadi saya berasumsi bahwa kode aktual melewati arus
entry
ke metode.Tetapi ada beberapa pertanyaan lagi:
BigInteger
kunci? Jika tidak kosong, mengapa Anda perlu mengisi daftar? Ketika sudah ada elemen dalam daftar, bukankah itu pembaruan atau perhitungan lain dalam kasus ini?Ini hanya beberapa pertanyaan yang muncul di benak saya ketika saya mencoba memahami fragmen kode. Jadi, menurut saya, itu adalah bau kode asli : Kode Anda tidak secara jelas mengomunikasikan maksudnya.
Ini bisa berarti ini ("semua atau tidak sama sekali" dan mencapai batas mengindikasikan kesalahan):
Atau itu bisa berarti ini ("perbarui hingga masalah pertama"):
Atau ini ("perbarui semua daftar tetapi simpan daftar asli jika terlalu besar"):
Atau bahkan yang berikut (menggunakan
computeFoos(…)
dari contoh pertama tetapi tanpa pengecualian):Atau itu bisa berarti sesuatu yang sama sekali berbeda ... ;-)
sumber