Komentar yang benar untuk menempatkan argumen fungsi boolean yang "salah"?


19

Dari beberapa proyek open source, saya mengumpulkan gaya pengkodean berikut

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

Saya selalu ragu tentang apa falseartinya di sini. Apakah ini berarti "lupa", atau "lupa" merujuk pada parameter yang sesuai (seperti dalam kasus di atas), dan "salah" dimaksudkan untuk meniadakannya?

Gaya apa yang paling sering digunakan dan apa cara terbaik (atau beberapa cara yang lebih baik) untuk menghindari ambiguitas?


38
gunakan enums (bahkan jika hanya ada 2 pilihan) sebagai ganti bools
Esailija

21
Beberapa bahasa mendukung argumen bernama. Dalam bahasa seperti itu, Anda bisa menggunakansomeFunction(forget: true);
Brian

3
Saya merasa berkewajiban untuk memberikan argumen Martin Fowler tentang Flag Arguments (juga berbicara tentang setter boolean). Secara umum, cobalah untuk menghindarinya.
FGreg

3
Untuk mengelabui yang jelas, komentar bisa berbohong. Dengan demikian selalu lebih baik untuk membuat kode Anda mendokumentasikan diri sendiri, karena beberapa akan berubah truemenjadi falsedan tidak memperbarui komentar. Jika Anda tidak dapat mengubah API, maka cara terbaik untuk berkomentar adalahsomeFunction( false /* true=forget, false=remember */)
Mark Lakata

1
@ Bakuriu - sebenarnya, saya mungkin masih memiliki API publik yang memiliki dua metode terpisah ( sortAscendingdan sortDescending, atau serupa). Sekarang, di dalam , keduanya dapat memanggil metode pribadi yang sama, yang mungkin memiliki parameter semacam ini. Sebenarnya, jika bahasa itu mendukungnya, mungkin yang akan saya sampaikan adalah fungsi lambda yang berisi arah penyortiran ...
Clockwork-Muse

Jawaban:


33

Dalam kode sampel yang Anda poskan, sepertinya forgetargumen flag. (Saya tidak bisa memastikan karena fungsinya murni hipotetis.)

Argumen bendera adalah bau kode. Mereka menunjukkan bahwa suatu fungsi melakukan lebih dari satu hal, dan fungsi yang baik seharusnya hanya melakukan satu hal.

Untuk menghindari argumen flag, pisahkan fungsi menjadi dua fungsi yang menjelaskan perbedaan dalam nama fungsi.

Bendera argumen

serveIceCream(bool lowFat)

Tidak ada argumen bendera

serveTraditionalIceCream()
serveLowFatIceCream()

sunting: Idealnya, Anda tidak perlu tetap menggunakan fungsi dengan parameter flag sama sekali. Ada kasus-kasus di sepanjang garis dari apa yang Fowler sebut implementasi kusut di mana sepenuhnya memisahkan fungsi-fungsi menciptakan kode duplikat. Namun, semakin tinggi kompleksitas siklomatik dari fungsi parameter, semakin kuat argumen untuk menghilangkannya.


Ini hanya firasat, tetapi parameter bernama forgetterdengar seperti iri fitur. Mengapa penelepon memberi tahu objek lain untuk melupakan sesuatu? Mungkin ada masalah desain yang lebih besar.


4
+17, helm menyala. Seperti apa bentuk tubuh serveTraditionalIceCreamdan serveLowFatIceCream? Saya memiliki enum dengan 14 jenis es krim.
JohnMark13

13
Untuk metode publik, konvensi ini baik, tetapi seperti disinggung oleh JohnMark, bagian lain dari SRP adalah "metode yang baik harus menjadi satu-satunya metode yang melakukan apa yang dilakukannya". Saya melihat ada varian N + 1 dari metode ini; N publik tanpa parameter, yang semuanya memanggil satu metode pribadi dengan parameter yang Anda coba hindari. Pada titik tertentu, Anda hanya menyerah dan mengekspos parameter sialan itu. Bau kode tidak harus berarti kode harus di refactored; mereka hanya hal-hal yang pantas dilihat kedua kali dalam tinjauan kode.
KeithS

@ Harun Dengan "fitur iri" apa maksudmu?
Geek

27

Dalam kata-kata awam:

  • false adalah literal.
  • Anda lulus secara literal false
  • Anda mengatakan someFunctionuntuk tidak melupakan
  • Anda mengatakan someFunctionbahwa parameter lupa adalahfalse
  • Anda mengatakan someFunctionuntuk mengingat

Menurut pendapat saya akan lebih baik jika fungsinya seperti ini:

void someFunction(bool remember);

Anda bisa menyebutnya

void ourFunction() {
  someFunction(true);
} 

atau simpan nama lama tetapi ubah fungsi wrapper Anda menjadi

void ourFunctionWithRemember() {
  someFunction(false);
} 

EDIT:

Seperti yang dikatakan @Vorac, selalu berusaha untuk menggunakan kata-kata positif. Negasi ganda membingungkan.


15
Memberi +1 pada gagasan untuk selalu berusaha menggunakan kata-kata positif. Negasi ganda membingungkan.
Vorac

1
Setuju, ide bagus. Memberitahu sistem untuk tidak melakukan sesuatu itu membingungkan. Jika menerima parameter boolean, ungkapkan secara positif.
Brandon

Dalam kebanyakan kasus, saya pikir Anda juga ingin lebih spesifik daripada remember, kecuali nama fungsi membuat arti yang remember sangat jelas. rememberToCleanUp* or *persistatau sesuatu.
itsbruce

14

Parameter mungkin bernama baik; sulit untuk mengatakannya tanpa mengetahui nama fungsinya. Saya berasumsi bahwa komentar itu ditulis oleh penulis asli dari fungsi, dan itu adalah pengingat dari apa yang lewat falseke dalam someFunctionberarti, tetapi bagi siapa saja yang datang bersama sesudahnya, itu sedikit tidak jelas pada pandangan pertama.

Menggunakan nama variabel positif (disarankan dalam Kode Selesai ) mungkin merupakan perubahan paling sederhana yang akan membuat potongan ini lebih mudah dibaca, misalnya

void someFunction(boolean remember);

kemudian ourFunctionmenjadi:

void ourFunction() {
    someFunction(true /* remember */);
}

Namun, menggunakan enum membuat pemanggilan fungsi lebih mudah dimengerti, dengan mengorbankan beberapa kode pendukung:

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

Jika Anda tidak dapat mengubah tanda tangan someFunctionuntuk alasan apa pun, menggunakan variabel sementara membuat kode lebih mudah dibaca juga, semacam menyederhanakan persyaratan dengan memperkenalkan variabel tanpa alasan lain selain membuat kode lebih mudah diurai oleh manusia. .

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
Menetapkan rememberke true berarti melupakan (dalam contoh Anda someFunction(true /* forget */);)?
Brian

2
The enumadalah jauh solusi terbaik. Hanya karena suatu tipe dapat direpresentasikan sebagai bool—yaitu, mereka isomorfis — itu tidak berarti bahwa itu harus direpresentasikan seperti itu. Argumen yang sama berlaku untuk stringdan bahkan int.
Jon Purdy

10

Ubah nama variabel sehingga nilai bool masuk akal.

Ini sejuta kali lebih baik daripada menambahkan komentar untuk menjelaskan argumen ke suatu fungsi karena namanya ambigu.


3
Itu tidak menjawab pertanyaan. Semua hal menjadi jelas ketika metode ini didefinisikan dan dipanggil dalam file yang sama, 4 baris terpisah. Tetapi bagaimana jika semua yang Anda lihat saat ini adalah penelepon? Bagaimana jika ia memiliki banyak boolean? Terkadang komentar in-line yang simpel sangat membantu.
Brandon

@Brandon Itu bukan argumen yang menolak memanggil boolean doNotPersist (atau, lebih baik, Bertahan). Menyebutnya "lupa" tanpa mengatakan apa yang harus dilupakan adalah terus terang tidak membantu. Oh, dan metode yang mengambil beberapa boolean sebagai opsi bau ke surga yang tinggi.
itsbruce

5

Buat boolean lokal dengan nama yang lebih deskriptif, dan berikan nilainya. Dengan begitu maknanya akan lebih jelas.

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

Jika Anda tidak dapat mengubah nama variabel, maka komentar harus sedikit lebih ekspresif:

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
Saran yang sangat bagus, tetapi saya tidak berpikir itu membahas masalah bahwa /* forget */komentar ada untuk mengatasi, yaitu bahwa tanpa deklarasi fungsi tepat di depan Anda, mungkin sulit untuk mengingat apa yang sedang diatur false. (Itulah sebabnya saya pikir saran @ Esailija untuk menambahkan enum lebih baik, dan mengapa saya menyukai bahasa yang memungkinkan parameter bernama.)
Gort the Robot

@StevenBurnap - terima kasih! Anda benar karena jawaban lama saya tidak cukup jelas dalam menjawab pertanyaan OP. Saya telah mengeditnya untuk membuatnya lebih jelas.

3

Ada artikel bagus yang menyebutkan situasi yang tepat ini karena merujuk ke Qt-Style APIs. Di sana, ini disebut The Boolean Parameter Trap dan layak dibaca.

Intinya adalah:

  1. Kelebihan fungsi sehingga bool tidak diperlukan lebih baik
  2. Menggunakan enum (seperti yang disarankan Esailija) adalah yang terbaik

2

Ini komentar yang aneh.

Dari perspektif kompiler, someFunction(false /* forget */);sebenarnya someFunction(false);(komentar dilucuti). Jadi, semua yang dilakukan adalah panggilan someFunctiondengan argumen pertama (dan satu-satunya) diatur ke false.

/* forget */hanya nama parameternya. Ini mungkin tidak lebih dari pengingat cepat (dan kotor), yang tidak benar-benar perlu ada di sana. Cukup gunakan nama parameter yang kurang ambigu, dan Anda tidak akan memerlukan komentar sama sekali.


1

Salah satu saran dari kode Bersih adalah untuk meminimalkan jumlah komentar yang tidak perlu 1 (karena mereka cenderung membusuk), dan untuk menyebutkan fungsi dan metode dengan benar.

Setelah itu, saya hanya akan menghapus komentar. Setelah semua, IDE modern (seperti gerhana) muncul kotak dengan kode ketika Anda meletakkan mouse di atas fungsi. Melihat kode harus menghapus ambiguitas.


1 Mengomentari beberapa kode kompleks tidak masalah.


btw Siapa bilang sesuatu seperti ini: "masalah programmer terburuk adalah bagaimana menamai variabel, dan mengimbanginya dengan satu"?
BЈовић

4
Anda kemungkinan mencari martinfowler.com/bliki/TwoHardThings.html sebagai sumbernya. Saya pernah mendengar tweak untuk "Hanya ada dua hal yang sulit dalam Ilmu Komputer: pembatalan cache, penamaan hal, dan dimatikan oleh satu kesalahan."

1

Untuk mengelabui yang jelas, komentar bisa berbohong. Jadi selalu lebih baik untuk membuat kode mendokumentasikan diri Anda tanpa menggunakan komentar untuk menjelaskan, karena beberapa orang (mungkin Anda) akan berubah trueke falsedan tidak memperbarui komentar.

Jika Anda tidak dapat mengubah API, maka saya menggunakan 2 opsi

  • Ubah komentar sehingga selalu benar, apa pun kodenya. Jika Anda hanya memanggil ini satu kali, ini adalah solusi yang baik, karena membuat dokumentasi tetap lokal.
     someFunction (false / * true = lupa, false = ingat * /); `
  • Gunakan #defines, terutama jika Anda menyebutnya lebih dari sekali.
     #define FORGET true
     #define INGAT salah
     someFunction (INGAT);

1

Saya suka jawaban tentang membuat komentar selalu benar , tetapi sementara bagus saya pikir itu merindukan masalah mendasar dengan kode ini - itu disebut dengan literal.

Anda harus menghindari penggunaan literal saat memanggil metode. Variabel lokal, parameter opsional, parameter bernama, enum - cara terbaik Anda menghindarinya akan tergantung pada bahasa dan apa yang tersedia, tetapi cobalah untuk menghindarinya. Literal memiliki nilai, tetapi mereka tidak memiliki makna.


-1

Di C #, saya menggunakan parameter bernama untuk membuat ini lebih jelas

someFunction(forget: false);

Atau enum:

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

Atau kelebihan:

someFunctionForget();

Atau polimorfisme`:

var foo = new Elephantine();

foo.someFunction();

-2

Penamaan harus selalu menyelesaikan ambiguitas untuk boolean. Saya selalu memberi nama boolean sesuatu seperti 'isThis' atau 'shouldDoThat', misalnya:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

dan seterusnya. Tetapi ketika Anda mereferensikan kode orang lain, yang terbaik adalah meninggalkan komentar ketika memberikan nilai.


bagaimana ini menjawab pertanyaan yang diajukan?
nyamuk

@gnat saya menjawab pertanyaannya tentang menyelesaikan ambiguitas dengan parameter boolean. Mungkin saya salah membaca pertanyaannya.
dchhetri
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.