Apakah salah menggunakan parameter boolean untuk menentukan nilai?

39

Menurut Apakah salah menggunakan parameter boolean untuk menentukan perilaku? , Saya tahu pentingnya menghindari menggunakan parameter boolean untuk menentukan perilaku, misalnya:

versi asli

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

versi baru:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

Tetapi bagaimana dengan kasus bahwa parameter boolean digunakan untuk menentukan nilai alih-alih perilaku? misalnya:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

Saya mencoba menghilangkan flag isHintOn dan membuat 2 fungsi terpisah:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

tetapi versi yang dimodifikasi tampaknya kurang dapat dipelihara karena:

  1. ini memiliki lebih banyak kode daripada versi aslinya

  2. tidak dapat dengan jelas menunjukkan bahwa visibilitas layer2 berlawanan dengan opsi petunjuk

  3. ketika layer baru (mis .: layer4) ditambahkan, saya perlu menambahkan

    this.layer4.visible=false;
    

    dan

    this.layer4.visible=true;  
    

    ke dalam setHintOn () dan setHintOff () secara terpisah

Jadi pertanyaan saya adalah, jika parameter boolean digunakan untuk menentukan nilai saja, tetapi bukan perilaku (misalnya: tidak ada jika-lain pada parameter itu), apakah masih disarankan untuk menghilangkan parameter boolean itu?

ocomfd
sumber
26
Tidak pernah salah jika kode yang dihasilkan lebih mudah dibaca dan dipelihara ;-) Saya akan merekomendasikan menggunakan metode tunggal alih-alih dua metode terpisah.
helb
32
Anda menyajikan argumen yang meyakinkan bahwa implementasi tunggal dari metode yang menetapkan boolean ini akan menghasilkan pemeliharaan kelas yang lebih mudah dan memahami implementasinya. Sangat baik; itu adalah pertimbangan yang sah. Tetapi antarmuka publik dari kelas tidak perlu cacat untuk mengakomodasi mereka. Jika metode terpisah akan membuat antarmuka publik lebih mudah dipahami dan digunakan, tentukan metode Anda setHint(boolean isHintOn)sebagai pribadi , dan tambahkan metode publik setHintOndan setHintOffyang masing-masing memanggil setHint(true)dan setHint(false).
Mark Amery
9
Saya akan sangat tidak senang dengan nama-nama metode: mereka tidak benar-benar menawarkan manfaat apa punsetHint(true|false) . Potahto kentang. Setidaknya gunakan sesuatu seperti setHintdan unsetHint.
Konrad Rudolph
4
@kevincline Jika kondisinya satu nama, Anda menulis isdi awal. isValiddll. Jadi mengapa mengubahnya menjadi dua kata? Selain itu, "lebih alami" ada di mata yang melihatnya. Jika Anda ingin mengucapkannya sebagai kalimat bahasa Inggris, maka bagi saya akan lebih alami untuk memiliki "jika petunjuknya ada pada" dengan "yang" dimasukkan.
Mr Lister

Jawaban:

95

Desain API harus fokus pada apa yang paling bisa digunakan untuk klien API, dari sisi panggilan .

Misalnya, jika API baru ini mengharuskan penelepon untuk menulis kode secara teratur seperti ini

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

maka harus jelas bahwa menghindari parameter lebih buruk daripada memiliki API yang memungkinkan pemanggil untuk menulis

 foo.setState(flag);

Versi sebelumnya hanya menghasilkan masalah yang kemudian harus diselesaikan di sisi panggilan (dan mungkin lebih dari sekali). Itu tidak meningkatkan keterbacaan atau pemeliharaan.

Sisi implementasi , bagaimanapun, seharusnya tidak menentukan bagaimana API publik terlihat. Jika fungsi seperti setHintdengan parameter membutuhkan lebih sedikit kode dalam implementasi, tetapi API dalam hal setHintOn/ setHintOffterlihat lebih mudah digunakan untuk klien, orang dapat menerapkannya dengan cara ini:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Jadi meskipun API publik tidak memiliki parameter boolean, tidak ada logika rangkap di sini, jadi hanya satu tempat untuk berubah ketika persyaratan baru (seperti dalam contoh pertanyaan) tiba.

Ini juga bekerja sebaliknya: jika setStatemetode dari atas perlu beralih di antara dua bagian kode yang berbeda, potongan-potongan kode itu dapat di-refactored ke dua metode pribadi yang berbeda. Jadi IMHO tidak masuk akal untuk mencari kriteria untuk memutuskan antara "satu parameter / satu metode" dan "nol parameter / dua metode" dengan melihat internal. Namun, perhatikan cara Anda ingin melihat API dalam peran konsumennya.

Jika ragu, coba gunakan "test driven development" (TDD), yang akan memaksa Anda untuk memikirkan API publik dan bagaimana menggunakannya.

Doc Brown
sumber
DocBrown, akankah Anda mengatakan garis pemisah yang tepat adalah apakah pengaturan setiap negara memiliki efek samping yang kompleks dan mungkin tidak dapat dibalikkan? Misalnya, jika Anda hanya mengibarkan bendera yang melakukan apa yang tertulis di kaleng, dan tidak ada mesin keadaan dasar, Anda akan membuat parameter status yang berbeda. Sedangkan, misalnya, Anda tidak akan membuat parameter metode seperti SetLoanFacility(bool enabled), karena setelah memberikan pinjaman, mungkin tidak mudah untuk membawanya lagi, dan dua opsi mungkin melibatkan logika yang sama sekali berbeda - dan Anda ingin pindah ke terpisah. Buat / Hapus metode.
Steve
15
@Steve: Anda masih mencoba mendesain API dari persyaratan yang Anda lihat di sisi implementasi. Terus terang: itu sama sekali tidak relevan. Gunakan varian API publik mana saja yang lebih mudah digunakan dari sisi panggilan. Secara internal, Anda selalu dapat membiarkan dua metode publik memanggil satu pribadi dengan parameter. Atau sebaliknya, Anda dapat membiarkan satu metode dengan saklar parameter antara dua metode pribadi dengan logika berbeda.
Doc Brown
@Steve: lihat edit saya.
Doc Brown
Saya mengambil semua poin Anda - saya benar-benar memikirkannya dari sisi penelepon (maka referensi saya untuk "apa yang tertulis di kaleng"), dan mencoba merumuskan aturan yang tepat di mana penelepon biasanya berharap untuk menggunakan setiap pendekatan. Tampak bagi saya bahwa aturannya adalah apakah penelepon mengharapkan panggilan berulang menjadi idempoten, dan transisi negara menjadi tidak dibatasi dan tanpa efek samping yang kompleks. Mengaktifkan dan menonaktifkan sakelar ruang akan diparameterisasi, mematikan dan mematikan pembangkit listrik regional akan menjadi multi-metode.
Steve
1
@Steve Jadi jika pengguna perlu menegaskan pengaturan tertentu maka Toggle()bukan fungsi yang benar untuk disediakan. Itulah intinya; jika penelepon hanya peduli tentang "ubah" dan bukan "apa yang berakhir dengan" maka Toggle()adalah opsi yang menghindari pemeriksaan tambahan dan pengambilan keputusan. Saya tidak akan menyebut bahwa itu kasus UMUM, saya juga tidak akan merekomendasikan membuatnya tersedia tanpa alasan yang baik, tetapi jika pengguna membutuhkan toggle maka saya akan memberi mereka toggle.
Kamil Drakari
40

Martin Fowler mengutip Kent Beck dalam merekomendasikan setOn() setOff()metode terpisah , tetapi juga mengatakan bahwa ini tidak boleh dianggap tidak dapat diganggu gugat:

Jika Anda menarik data dari sumber boolean, seperti kontrol UI atau sumber data, saya lebih suka memiliki setSwitch(aValue)daripada

if (aValue)
  setOn();
else
  setOff();

Ini adalah contoh bahwa API harus ditulis untuk memudahkan penelepon, jadi jika kita tahu dari mana penelepon berasal kita harus merancang API dengan informasi itu dalam pikiran. Ini juga berpendapat bahwa kami terkadang menyediakan kedua gaya jika kami menerima penelepon dengan kedua cara.

Rekomendasi lain adalah dengan menggunakan nilai enumerasi atau tipe bendera untuk memberi truedan falselebih baik, nama spesifik konteks. Dalam contoh Anda, showHintdan hideHintbisa lebih baik.

Graham Lee
sumber
16
Dalam pengalaman saya, setSwitch (nilai) hampir selalu menghasilkan kode keseluruhan kurang dari setOn / setOff justru karena kode if / else dikutip dalam jawaban Anda. Saya cenderung mengutuk pengembang yang memberi saya API setOn / setOff daripada setSwitch (nilai).
17 dari 26
1
Melihatnya dari lensa yang sama: Jika Anda perlu men-hardcode berapa nilainya, mudah juga. Namun, jika Anda perlu mengaturnya, katakanlah, input pengguna, jika Anda bisa langsung memberikan nilai, itu menyimpan langkah.
Nic Hartley
@ 17of26 sebagai counterexample konkret (untuk menunjukkan bahwa "itu tergantung" daripada yang satu lebih disukai dari yang lain), di AppKit Apple ada -[NSView setNeedsDisplay:]metode di mana Anda lulus YESjika tampilan harus digambar ulang dan NOjika tidak. Anda hampir tidak perlu mengatakannya untuk tidak, jadi UIKit hanya memiliki -[UIView setNeedsDisplay]tanpa parameter. Itu tidak memiliki -setDoesNotNeedDisplaymetode yang sesuai .
Graham Lee
2
@ GrahamLee, saya pikir argumen Fowler cukup halus dan sangat bergantung pada penilaian. Saya tidak akan menggunakan bool isPremiumflag dalam contohnya, tetapi saya akan menggunakan enum ( BookingType bookingType) untuk parameterisasi metode yang sama, kecuali jika logika untuk setiap pemesanan cukup berbeda. "Logika kusut" yang dirujuk oleh Fowler seringkali diinginkan jika seseorang ingin dapat melihat apa perbedaan antara kedua mode. Dan jika mereka sangat berbeda, saya akan mengekspos metode parameterised secara eksternal, dan mengimplementasikan metode terpisah secara internal.
Steve
3

Saya pikir Anda mencampur dua hal dalam posting Anda, API dan implementasinya. Dalam kedua kasus saya tidak berpikir ada aturan yang kuat yang dapat Anda gunakan sepanjang waktu, tetapi Anda harus mempertimbangkan dua hal ini secara mandiri (sebanyak mungkin).

Mari kita mulai dengan API, keduanya:

public void setHint(boolean isHintOn)

dan:

public void setHintOn()
public void setHintOff()

adalah alternatif yang valid tergantung pada objek yang seharusnya Anda tawarkan dan bagaimana klien Anda akan menggunakan API. Seperti yang ditunjukkan Doc, jika pengguna Anda sudah memiliki variabel Boolean (dari kontrol UI, aksi pengguna, eksternal, API, dll) opsi pertama lebih masuk akal, jika tidak, Anda hanya memaksakan pernyataan tambahan jika pernyataan pada kode klien . Namun, jika misalnya Anda mengubah petunjuk menjadi true saat memulai proses dan menjadi false di akhir opsi pertama memberi Anda sesuatu seperti ini:

setHint(true)
// Do your process
…
setHint(false)

sedangkan opsi kedua memberi Anda ini:

setHintOn()
// Do your process
…
setHintOff()

IMO mana yang jauh lebih mudah dibaca, jadi saya akan memilih opsi kedua dalam hal ini. Jelas, tidak ada yang menghentikan Anda dari menawarkan kedua opsi (atau lebih, Anda bisa menggunakan enum seperti kata Graham jika itu lebih masuk akal misalnya).

Intinya adalah Anda harus memilih API berdasarkan apa yang seharusnya dilakukan oleh objek dan bagaimana klien akan menggunakannya, bukan berdasarkan pada bagaimana Anda akan mengimplementasikannya.

Maka Anda harus memilih bagaimana Anda mengimplementasikan API publik Anda. Katakanlah kami memilih metode setHintOndan setHintOffsebagai API publik kami dan mereka membagikan logika umum ini seperti dalam contoh Anda. Anda dapat dengan mudah mengabstraksi logika ini melalui metode pribadi (kode disalin dari Doc):

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Sebaliknya, katakanlah kami memilih setHint(boolean isHintOn)API kami tetapi mari balikkan contoh Anda, karena alasan apa pun yang mengatur petunjuk On sama sekali berbeda dengan menyetelnya ke Off. Dalam hal ini kita bisa menerapkannya sebagai berikut:

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

Atau bahkan:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

Intinya adalah bahwa, dalam kedua kasus, kami pertama-tama memilih API publik kami dan kemudian mengadaptasi implementasi kami agar sesuai dengan API yang dipilih (dan kendala yang kami miliki), bukan sebaliknya.

Ngomong-ngomong, saya pikir hal yang sama berlaku untuk posting yang Anda tautkan tentang menggunakan parameter boolean untuk menentukan perilaku, yaitu Anda harus memutuskan berdasarkan pada use case khusus Anda daripada beberapa aturan keras (meskipun dalam kasus tertentu biasanya hal yang benar untuk dilakukan rusak dalam beberapa fungsi).

jesm00
sumber
3
Sebagai catatan tambahan, jika itu seperti pseudo-block (satu pernyataan di awal, satu di akhir), Anda mungkin harus menggunakan begindan endatau sinonim, hanya untuk membuatnya secara eksplisit menjelaskan apa yang mereka lakukan, dan untuk menyiratkan bahwa awal harus memiliki akhir dan sebaliknya.
Nic Hartley
Saya setuju dengan Nic, dan akan menambahkan: Jika Anda ingin memastikan awal dan akhir selalu digabungkan, Anda juga harus memberikan idiom khusus bahasa untuk itu: RAII / penjaga lingkup di C ++, usingblok di C #, withpernyataan konteks manajer dengan Python, meneruskan tubuh sebagai lambda atau objek yang dapat dipanggil (mis. sintaksis blok Ruby), dll.
Daniel Pryden
Saya setuju dengan kedua poin, saya hanya mencoba memberikan contoh sederhana untuk menggambarkan kasus lain (meskipun ini adalah contoh buruk seperti yang Anda berdua tunjukkan :)).
jesm00
2

Hal pertama yang pertama: kode tidak secara otomatis kurang dapat dipelihara, hanya karena itu sedikit lebih lama. Kejelasan adalah yang terpenting.

Sekarang, jika Anda benar - benar hanya berurusan dengan data, maka apa yang Anda miliki adalah setter untuk properti boolean. Dalam hal ini Anda mungkin ingin hanya menyimpan nilai itu secara langsung dan mendapatkan visibilitas lapisan, yaitu

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

(Saya telah mengambil kebebasan untuk memberi layer nama sebenarnya - jika Anda tidak memiliki ini dalam kode asli Anda, saya akan mulai dengan itu)

Ini masih menyisakan pertanyaan apakah memiliki setHintVisibility(bool)metode. Secara pribadi, saya akan merekomendasikan menggantinya dengan metode showHint()dan hideHint()- keduanya akan sangat sederhana dan Anda tidak perlu mengubahnya ketika Anda menambahkan lapisan. Namun, ini bukan pemotongan yang benar / salah.

Sekarang jika memanggil fungsi seharusnya benar-benar mengubah visibilitas lapisan-lapisan itu, Anda sebenarnya memiliki perilaku. Dalam hal ini, saya pasti akan merekomendasikan metode terpisah.

dua kali kamu
sumber
Jadi tl; dr adalah: Anda merekomendasikan pemisahan menjadi dua fungsi karena Anda tidak perlu mengubahnya ketika Anda menambahkan lapisan? Jika layer4 ditambahkan, kita mungkin perlu mengatakan "this.layer4.visibility = isHintOn" juga, jadi saya tidak yakin saya setuju. Jika ada, itu adalah demerit terhadapnya, karena sekarang ketika layer ditambahkan kita harus mengedit dua fungsi, bukan hanya satu.
Erdrik Ironrose
Tidak, saya (lemah) merekomendasikannya untuk kejelasan ( showHintvs setHintVisibility). Saya menyebutkan layer baru hanya karena OP khawatir tentang hal itu. Juga, kita hanya perlu menambahkan satu metode baru: isLayer4Visible. showHintdan hideHinthanya mengatur isHintVisibleatribut ke true / false dan itu tidak berubah.
gandakan kamu
1
@doubleYou, katamu kode yang lebih panjang tidak otomatis kurang bisa dirawat. Saya akan mengatakan bahwa panjang kode adalah salah satu variabel utama dalam pemeliharaan dan kejelasan, hanya ditimpa oleh kompleksitas struktural. Kode apa pun yang menjadi lebih panjang dan lebih kompleks secara struktural harus pantas mendapatkannya, jika tidak masalah sederhana akan menerima perlakuan yang lebih kompleks dalam kode daripada yang seharusnya, dan basis kode mendapatkan setumpuk baris yang tidak perlu (masalah "terlalu banyak tingkat tipuan").
Steve
@Steve Saya sepenuhnya setuju bahwa Anda dapat merekayasa berlebihan hal-hal, yaitu membuat kode lebih lama tanpa membuatnya lebih jelas - otoh, Anda selalu dapat membuat kode lebih pendek dengan biaya kejelasan, jadi tidak ada hubungan 1: 1 di sini.
gandakan kamu
@ Steve Pikirkan “kode golf” - mengambil kode tersebut dan menulis ulang dalam garis lebih tidak biasanya membuat lebih jelas. "Golf kode" adalah ekstrem, tetapi masih ada banyak programmer di luar sana yang berpikir menjejalkan semuanya menjadi satu ekspresi pintar adalah "elegan" dan mungkin bahkan lebih cepat karena kompiler tidak mengoptimalkan cukup baik.
BlackJack
1

Parameter Boolean baik-baik saja dalam contoh kedua. Seperti yang sudah Anda ketahui, parameter boolean tidak dengan sendirinya bermasalah. Ini mengubah perilaku berdasarkan pada bendera, yang bermasalah.

Contoh pertama bermasalah, karena penamaan menunjukkan metode setter, tetapi implementasi tampaknya menjadi sesuatu yang berbeda. Jadi, Anda memiliki antipattern switching perilaku, dan metode yang disebut menyesatkan. Tetapi jika metode ini sebenarnya adalah setter biasa (tanpa pergantian perilaku), maka tidak ada masalah dengan setState(boolean). Memiliki dua metode, setStateTrue()dan setStateFalse()hanya hal-hal rumit yang tidak perlu tanpa manfaat.

JacquesB
sumber
1

Cara lain untuk memecahkan masalah ini adalah dengan memperkenalkan objek untuk mewakili setiap petunjuk, dan membuat objek bertanggung jawab untuk menentukan nilai-nilai boolean yang terkait dengan petunjuk itu. Dengan cara ini Anda bisa menambahkan permutasi baru daripada hanya memiliki dua status boolean.

Misalnya, di Jawa, Anda dapat melakukan:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

Dan kemudian kode penelepon Anda akan terlihat seperti ini:

setHint(HintState.SHOW_HINT);

Dan kode implementasi Anda akan terlihat seperti ini:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

Hal ini membuat kode implementasi dan kode pemanggil ringkas, sebagai imbalan untuk mendefinisikan tipe data baru yang dengan jelas memetakan niat yang diketik dengan kuat, yang dinamai dengan set negara yang sesuai. Saya pikir itu lebih baik di sekitar.

Daniel Pryden
sumber
0

Jadi pertanyaan saya adalah, jika parameter boolean digunakan untuk menentukan nilai saja, tetapi bukan perilaku (misalnya: tidak ada jika-lain pada parameter itu), apakah masih disarankan untuk menghilangkan parameter boolean itu?

Ketika saya memiliki keraguan tentang hal-hal seperti itu. Saya suka menggambarkan seperti apa jejak tumpukan itu.

Selama bertahun-tahun saya bekerja pada proyek PHP yang menggunakan fungsi yang sama dengan setter dan pengambil . Jika Anda lulus nol, itu akan mengembalikan nilai, jika tidak setel. Sangat mengerikan untuk bekerja dengannya .

Berikut ini contoh jejak tumpukan:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

Anda tidak tahu keadaan internal dan itu membuat debugging lebih sulit. Ada banyak efek samping negatif lainnya, tetapi cobalah untuk melihat ada nilai dalam nama fungsi verbose dan kejelasan ketika fungsi melakukan satu tindakan.

Sekarang gambar bekerja dengan jejak stack untuk fungsi yang memiliki perilaku A atau B berdasarkan nilai boolean.

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

Itu membingungkan jika Anda bertanya kepada saya. Garis yang sama setVisiblemenghasilkan dua jalur jejak yang berbeda.

Jadi kembalilah ke pertanyaan Anda. Coba gambarkan seperti apa jejak tumpukan itu, bagaimana komunikasinya kepada seseorang tentang apa yang terjadi dan tanyakan pada diri Anda apakah Anda membantu orang yang akan datang men-debug kode.

Berikut beberapa tips:

  • nama fungsi yang jelas yang menyiratkan maksud tanpa perlu tahu nilai argumen.
  • suatu fungsi melakukan satu tindakan
  • namanya menyiratkan mutasi atau perilaku tidak berubah
  • memecahkan tantangan debugging relatif terhadap kemampuan debugging bahasa dan alat-alat.

Terkadang kode terlihat berlebihan di bawah mikroskop, tetapi ketika Anda menarik kembali ke gambar yang lebih besar pendekatan minimalis akan membuatnya menghilang. Jika Anda perlu menonjol agar Anda dapat mempertahankannya. Menambahkan banyak fungsi kecil mungkin terasa terlalu bertele-tele, tetapi itu meningkatkan pemeliharaan ketika digunakan secara luas dalam konteks yang lebih besar.

Reactgular
sumber
1
Yang membingungkan bagi saya tentang setVisiblejejak stack adalah bahwa panggilan setVisible(true)muncul menghasilkan panggilan ke setVisible(false)(atau sebaliknya, tergantung pada bagaimana Anda membuat jejak itu terjadi).
David K
0

Di hampir setiap kasus di mana Anda mengirimkan booleanparameter ke metode sebagai bendera untuk mengubah perilaku sesuatu, Anda harus mempertimbangkan yang lebih eksplisit dan aman untuk melakukan hal ini.

Jika Anda melakukan tidak lebih dari menggunakan Enum yang mewakili negara Anda telah meningkatkan pemahaman kode Anda.

Contoh ini menggunakan Nodekelas dari JavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

selalu lebih baik daripada, seperti yang ditemukan pada banyak JavaFXobjek:

public void setVisiblity(final boolean flag);

tapi saya pikir .setVisible()dan .setHidden()merupakan solusi terbaik untuk situasi di mana bendera adalahboolean karena ini adalah yang paling eksplisit dan paling tidak bertele-tele.

dalam hal sesuatu dengan banyak pilihan, ini bahkan lebih penting untuk dilakukan dengan cara ini. EnumSetada hanya karena alasan ini.

Ed Mann memiliki postingan blog yang sangat bagus tentang hal ini. Saya akan memparafrasakan apa yang dia katakan, jadi jangan menduplikasi usaha saya hanya akan memposting link ke posting blognya sebagai tambahan untuk jawaban ini.


sumber
0

Ketika memutuskan antara pendekatan untuk beberapa antarmuka yang melewati parameter (boolean) vs metode kelebihan beban tanpa parameter tersebut, lihat ke klien yang mengkonsumsi.

Jika semua penggunaan akan melewati nilai konstan (misalnya benar, salah) maka itu berpendapat untuk kelebihan.

Jika semua penggunaan akan melewati nilai variabel maka itu berargumen untuk metode dengan pendekatan parameter.

Jika tidak satu pun dari kedua ekstrem itu yang berlaku, itu berarti ada campuran penggunaan klien, jadi Anda harus memilih apakah akan mendukung kedua bentuk, atau membuat satu kategori klien untuk beradaptasi dengan yang lain (yang bagi mereka merupakan gaya yang lebih tidak wajar).

Erik Eidt
sumber
Bagaimana jika Anda sedang merancang sebuah sistem yang terintegrasi dan Anda berada pada akhirnya kedua produsen kode dan "memakan klien" kode? Bagaimana seharusnya seseorang yang berdiri di posisi klien mengkonsumsi merumuskan preferensi mereka untuk satu pendekatan di atas yang lain?
Steve
@Steve, Sebagai klien yang mengonsumsi, Anda tahu apakah Anda melewati konstanta atau variabel. Jika melewati konstanta, lebih suka kelebihan tanpa parameter.
Erik Eidt
Tapi saya tertarik untuk mengartikulasikan mengapa itu harus terjadi. Mengapa tidak menggunakan enum untuk konstanta dalam jumlah terbatas, karena itu adalah sintaksis ringan di sebagian besar bahasa yang dirancang tepat untuk tujuan semacam ini?
Steve
@Steve, jika kita tahu pada waktu desain / waktu kompilasi bahwa klien akan menggunakan nilai konstan (benar / salah) dalam semua kasus, maka itu menunjukkan bahwa sebenarnya ada dua metode spesifik yang berbeda daripada satu metode umum (yang mengambil parameter). Saya akan berdebat menentang memperkenalkan generalisasi metode parameter ketika itu tidak digunakan - itu adalah argumen YAGNI.
Erik Eidt
0

Ada dua pertimbangan untuk mendesain:

  • API: antarmuka apa yang Anda tampilkan kepada pengguna,
  • Implementasi: kejelasan, pemeliharaan, dll ...

Mereka seharusnya tidak digabung.

Tidak apa-apa untuk:

  • memiliki beberapa metode dalam delegasi API ke satu implementasi,
  • memiliki satu metode dalam pengiriman API ke beberapa implementasi tergantung pada kondisinya.

Dengan demikian, argumen apa pun yang berusaha menyeimbangkan biaya / manfaat desain API dengan biaya / manfaat desain implementasi diragukan dan harus diperiksa dengan cermat.


Di sisi API

Sebagai seorang programmer, saya biasanya akan mendukung API yang dapat diprogram. Kode jauh lebih jelas ketika saya bisa meneruskan nilai daripada ketika saya membutuhkan if/ switchpernyataan pada nilai untuk memutuskan fungsi mana yang harus dipanggil.

Yang terakhir mungkin diperlukan jika setiap fungsi mengharapkan argumen yang berbeda.

Jadi, dalam kasus Anda, satu metode setState(type value) tampak lebih baik.

Namun , ada yang lebih buruk daripada tanpa nama true, false, 2, dll ... nilai-nilai sihir tidak memiliki arti sendiri. Hindari obsesi primitif , dan merangkul ketikan yang kuat.

Dengan demikian, dari API POV, saya ingin: setState(State state).


Di sisi implementasi

Saya sarankan melakukan apa pun yang lebih mudah.

Jika metodenya sederhana, sebaiknya tetap bersama. Jika aliran kontrol berbelit-belit, yang terbaik untuk memisahkannya dalam beberapa metode, masing-masing berurusan dengan sub-kasus atau langkah pipa.


Akhirnya, pertimbangkan pengelompokan .

Dalam contoh Anda (dengan spasi kosong yang dapat dibaca):

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

Mengapa layer2merusak tren? Apakah ini fitur atau bug?

Mungkinkah memiliki 2 daftar [layer1, layer3]dan [layer2], dengan eksplisit nama menunjukkan alasan mereka dikelompokkan bersama, dan kemudian beralih ke daftar itu.

Sebagai contoh:

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

Kode berbicara sendiri, jelas mengapa ada dua kelompok dan perilaku mereka berbeda.

Matthieu M.
sumber
0

Terpisah dari pertanyaan setOn() + setOff()vs set(flag), saya akan mempertimbangkan dengan hati-hati apakah jenis boolean terbaik di sini. Apakah Anda yakin tidak akan ada opsi ketiga?

Mungkin lebih baik mempertimbangkan enum daripada boolean. Selain memungkinkan ekstensibilitas, ini juga mempersulit mendapatkan boolean dengan cara yang salah, misalnya:

setHint(false)

vs.

setHint(Visibility::HIDE)

Dengan enum, akan jauh lebih mudah untuk diperpanjang ketika seseorang memutuskan mereka menginginkan opsi 'jika diperlukan':

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

vs.

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New
Phil
sumber
0

Menurut ..., saya tahu pentingnya menghindari menggunakan parameter boolean untuk menentukan perilaku

Saya akan menyarankan untuk mengevaluasi kembali pengetahuan ini.

Pertama-tama, saya tidak melihat kesimpulan yang Anda usulkan dalam pertanyaan SE yang Anda tautkan. Mereka kebanyakan berbicara tentang meneruskan parameter melalui beberapa langkah pemanggilan metode, di mana ia dievaluasi sangat jauh di rantai.

Dalam contoh Anda, Anda mengevaluasi parameter tepat di metode Anda. Dalam hal itu, tidak ada perbedaan sama sekali dari jenis parameter lainnya.

Secara umum, sama sekali tidak ada yang salah dengan menggunakan parameter boolean; dan tentu saja parameter apa pun akan menentukan perilaku, atau mengapa Anda harus memilikinya?

AnoE
sumber
0

Mendefinisikan pertanyaan

Pertanyaan judul Anda adalah "Apakah salah dengan [...]?" - tapi apa maksudmu dengan "salah"?

Menurut kompiler C # atau Java, itu tidak salah. Saya yakin Anda sadar akan hal itu dan bukan itu yang Anda minta. Saya takut selain itu, kami hanya punya pendapat berbeda nprogrammer n+1. Jawaban ini menyajikan apa yang dikatakan buku Kode Bersih tentang hal ini.

Menjawab

Kode Bersih membuat kasus yang kuat terhadap argumen fungsi secara umum:

Argumennya sulit. Mereka mengambil banyak kekuatan konseptual. [...] pembaca kami harus menafsirkannya setiap kali mereka melihatnya.

"Pembaca" di sini bisa menjadi konsumen API. Ini juga bisa menjadi pembuat kode berikutnya, yang belum tahu apa yang kode ini lakukan - yang bisa Anda lakukan dalam sebulan. Mereka akan melalui 2 fungsi secara terpisah atau melalui 1 fungsi dua kali , sekali mengingat truedan sekali falsedalam pikiran.
Singkatnya, gunakan argumen sesedikit mungkin .

Kasus spesifik argumen bendera kemudian ditangani secara langsung:

Argumen bendera jelek. Melewati boolean menjadi sebuah fungsi adalah praktik yang benar-benar mengerikan. Ini segera merumitkan tanda tangan metode, dengan keras menyatakan bahwa fungsi ini melakukan lebih dari satu hal. Ia melakukan satu hal jika bendera itu benar dan yang lain jika bendera itu salah!

Untuk menjawab pertanyaan Anda secara langsung:
Menurut Clean Code , Disarankan untuk menghilangkan parameter itu.


Informasi tambahan:

Contoh Anda agak sederhana, tetapi bahkan di sana Anda dapat melihat kesederhanaan menyebar ke kode Anda: Fungsi tanpa-parameter hanya melakukan penugasan sederhana, sedangkan fungsi lainnya harus melakukan aritmatika boolean untuk mencapai tujuan yang sama. Ini adalah aritmatika boolean yang sepele dalam contoh sederhana ini, tetapi mungkin cukup rumit dalam situasi nyata.


Saya telah melihat banyak argumen di sini bahwa Anda harus membuatnya bergantung pada pengguna API, karena harus melakukan ini di banyak tempat akan menjadi bodoh:

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

Saya tidak setuju bahwa sesuatu yang non-optimal yang terjadi di sini. Mungkin terlalu jelas untuk dilihat, tetapi kalimat pertama Anda menyebutkan "pentingnya menghindari [sic] menggunakan parameter boolean untuk menentukan perilaku" dan itulah dasar untuk seluruh pertanyaan. Saya tidak melihat alasan untuk membuat hal yang buruk untuk dilakukan lebih mudah bagi pengguna API.


Saya tidak tahu apakah Anda melakukan pengujian - dalam hal ini, pertimbangkan juga ini:

Argumen bahkan lebih sulit dari sudut pandang pengujian. Bayangkan kesulitan menulis semua kasus uji untuk memastikan bahwa semua kombinasi berbagai argumen berfungsi dengan baik. Jika tidak ada argumen, ini sepele.

R. Schmitz
sumber
Anda telah benar-benar mengubur lede di sini: "... kalimat pertama Anda menyebutkan" pentingnya menghindari [sic] menggunakan parameter boolean untuk menentukan perilaku "dan itu adalah dasar untuk seluruh pertanyaan. Saya tidak melihat alasan untuk membuat hal yang buruk untuk dilakukan lebih mudah bagi pengguna API. " Ini adalah poin yang menarik, tetapi Anda agak merusak argumen Anda sendiri di paragraf terakhir Anda.
Wildcard
Mengubur lede? Inti dari jawaban ini adalah "gunakan argumen sesedikit mungkin", yang dijelaskan pada paruh pertama jawaban ini. Semuanya setelah itu hanyalah info tambahan: menyangkal argumen yang bertentangan (oleh pengguna lain, bukan OP) dan sesuatu yang tidak berlaku untuk semua orang.
R. Schmitz
Paragraf terakhir hanya mencoba menjelaskan bahwa pertanyaan judul tidak cukup jelas untuk dijawab. OP bertanya apakah itu "salah", tetapi tidak mengatakan sesuai dengan siapa atau apa. Menurut Kompiler? Sepertinya kode yang valid, jadi tidak salah. Menurut buku Clean Code? Ini menggunakan argumen bendera, jadi ya itu "salah". Namun, saya menulis "disarankan" sebagai gantinya, karena pragmatis> dogmatis. Apakah Anda pikir saya perlu membuatnya lebih jelas?
R. Schmitz
jadi tunggu, pembelaan Anda terhadap jawaban Anda adalah bahwa pertanyaan judul terlalu tidak jelas untuk dijawab? : D Oke ... Saya benar-benar berpikir bahwa poin yang saya kutip adalah pengambilan segar yang menarik.
Wildcard
1
Jelas sekarang; kerja bagus!
Wildcard