Apakah mendefinisikan variabel untuk menyebutkan argumen metode merupakan praktik yang baik?


73

Demi keterbacaan, saya sering menemukan diri saya mendefinisikan variabel sementara saat memanggil fungsi, seperti kode berikut

var preventUndo = true;
doSomething(preventUndo);

Versi yang lebih pendek dari ini adalah:

doSomething(true);

Tetapi ketika saya kembali ke kode saya sering bertanya-tanya apa yang truedimaksud. Apakah ada konvensi untuk teka-teki seperti ini?


5
Apakah Anda menggunakan semacam IDE? Sebagian besar akan memiliki semacam cara untuk menunjukkan apa parameter untuk fungsi yang Anda panggil yang agak membatalkan kebutuhan untuk melakukan ini.
Matthew Scharley

4
Saya tidak menggunakan IDE, tetapi meskipun begitu saya kira petunjuk itu akan memerlukan semacam tindakan (mouseover atau pindah ke garis). Saya ingin tahu apa yang kode saya lakukan hanya dengan melihatnya.
methodofaction

11
Jika bahasa mendukungnya, Anda dapat menggunakan enumerasi sepertidoSomething( Undo.PREVENT )
James P.

4
Tapi Anda bisa mendefinisikan Undo = { PREVENT = true, DONT_PREVENT = false }. Tetapi dalam JavaScript, konvensi adalah melakukannya seperti itu: function myFunction( mandatoryArg1, mandatoryArg2, otherArgs ) { /*...*/ }lalu myFunction( 1, 2, { option1: true, option2: false } ).
xavierm02

6
Itu pasti tergantung pada bahasanya. Jika ini python, misalnya, saya sarankan hanya menggunakan argumen kata kunci, misalnyadoSomething(preventUndo=True)
Joel Cornett

Jawaban:


117

Menjelaskan Variabel

Kasing Anda adalah contoh dari pengantar menjelaskan variabel refactoring. Singkatnya, variabel yang menjelaskan adalah variabel yang tidak sepenuhnya diperlukan, tetapi memungkinkan Anda untuk memberikan nama yang jelas untuk sesuatu, dengan tujuan meningkatkan keterbacaan.

Kode berkualitas baik mengkomunikasikan maksud kepada pembaca; dan sebagai keterbacaan pengembang profesional dan rawatan adalah tujuan # 1 Anda.

Dengan demikian, aturan praktis yang saya sarankan adalah ini: jika tujuan parameter Anda tidak segera jelas, jangan ragu untuk menggunakan variabel untuk memberikannya nama baik. Saya pikir ini adalah praktik yang baik secara umum (kecuali disalahgunakan). Berikut adalah contoh cepat dan dibuat-buat - pertimbangkan:

editButton.Enabled = (_grid.SelectedRow != null && ((Person)_grid.SelectedRow).Status == PersonStatus.Active);

versus yang sedikit lebih panjang, tetapi bisa dibilang lebih jelas:

bool personIsSelected = (_grid.SelectedRow != null);
bool selectedPersonIsEditable = (personIsSelected && ((Person)_grid.SelectedRow).Status == PersonStatus.Active)
editButton.Enabled = (personIsSelected && selectedPersonIsEditable);

Parameter Boolean

Contoh Anda sebenarnya menyoroti mengapa boolean di API sering merupakan ide yang buruk - di sisi panggilan, mereka tidak melakukan apa pun untuk menjelaskan apa yang terjadi. Mempertimbangkan:

ParseFolder(true, false);

Anda harus mencari tahu apa arti parameter tersebut; jika mereka enum, itu akan jauh lebih jelas:

ParseFolder(ParseBehaviour.Recursive, CompatibilityOption.Strict);

Sunting:

Ditambahkan judul dan bertukar urutan dua paragraf utama, karena terlalu banyak orang yang berfokus pada bagian parameter boolean (untuk menjadi adil, itu adalah paragraf pertama awalnya). Juga menambahkan contoh ke bagian pertama.


28
Kecuali Anda menggunakan parameter bernama (argumen bernama dalam .NET Framework), dalam hal doSomething(preventUndo: true);ini cukup jelas. Juga, membuat enum untuk setiap boolean di API Anda? Serius?
Arseni Mourzenko

12
@MainMa: Jika bahasa yang Anda gunakan tidak cukup dingin untuk mendukung argumen bernama, menggunakan enum adalah pilihan yang baik. Ini tidak berarti bahwa Anda harus memperkenalkan enum secara sistematis di mana-mana.
Giorgio

18
@MainMa - Untuk menjawab pertanyaan itu, intinya adalah Anda seharusnya tidak memiliki boolean di API Anda (setidaknya, yang publik). Anda perlu mengubah ke sintaks argumen bernama, yang bisa dibilang tidak idiomatik (pada saat penulisan), dan dalam hal apa pun, parameter boolean hampir selalu menandakan bahwa metode Anda melakukan dua hal, dan Anda membiarkan penelepon untuk memilih. Tetapi inti dari jawaban saya adalah bahwa tidak ada yang salah dengan menggunakan variabel yang menjelaskan dalam kode Anda; itu sebenarnya praktik yang sangat bagus.
Daniel B

3
Ini adalah bagian permanen dari bahasa C #, apa lagi "penerimaan arus utama" yang dibutuhkan? Bahkan untuk seseorang yang belum mengetahui konstruk (yang berarti C # devs Anda bahkan belum membaca C # docs), cukup jelas apa fungsinya.
Nate CK

3
Itulah sebabnya MS merilis ringkasan bagus dari semua hal baru yang telah mereka tambahkan di setiap versi, saya tidak berpikir itu menciptakan banyak pekerjaan untuk membaca salah satu dari ini setiap beberapa tahun: msdn.microsoft.com/en- us / library / bb383815% 28v = vs.100% 29.aspx
Nate CK

43

Jangan menulis kode yang tidak Anda butuhkan.

Jika Anda merasa doSomething(true)sulit untuk dipahami, Anda harus menambahkan komentar:

// Do something and prevent the undo.
doSomething(true);

atau, jika bahasa mendukungnya, tambahkan nama parameter:

doSomething(preventUndo: true);

Jika tidak, andalkan IDE Anda untuk memberi Anda tanda tangan dari metode yang dipanggil:

masukkan deskripsi gambar di sini

Kasus di mana itu berguna

Menempatkan variabel tambahan dapat bermanfaat:

  1. Untuk keperluan debugging:

    var productId = this.Data.GetLastProductId();
    this.Data.AddToCart(productId);
    

    Kode yang sama dapat ditulis dalam satu baris, tetapi jika Anda ingin meletakkan breakpoint sebelum menambahkan produk ke troli untuk melihat apakah id produk benar, menulis dua baris bukan satu adalah ide yang baik.

  2. Jika Anda memiliki metode dengan banyak parameter dan setiap parameter berasal dari evaluasi. Dalam satu baris, ini mungkin menjadi sama sekali tidak dapat dibaca.

    // Even with indentation, this is unreadable.
    var doSomething(
        isSomethingElse ? 0 : this.getAValue(),
        this.getAnotherOne() ?? this.default,
        (a + b + c + d + e) * f,
        this.hello ? this.world : (this.hello2 ? this.world2 : -1));
    
  3. Jika evaluasi suatu parameter terlalu rumit. Contoh kode yang pernah saya lihat:

    // Wouldn't it be easier to have several if/else's (maybe even in a separate method)?
    do(something ? (hello ? world : -1) : (programmers ? stackexchange : (com ? -1 : 0)));
    

Kenapa tidak dalam kasus lain?

Mengapa Anda tidak membuat variabel tambahan dalam kasus sederhana?

  1. Bukan karena dampak kinerja. Ini akan menjadi asumsi yang sangat salah dari pengembang pemula yang mengoptimalkan aplikasi secara mikro . Tidak ada dampak kinerja di sebagian besar bahasa, karena kompiler akan menyejajarkan variabel. Dalam bahasa-bahasa di mana kompiler tidak melakukan itu, Anda mungkin mendapatkan beberapa mikrodetik dengan menggariskannya dengan tangan, yang tidak sepadan. Jangan lakukan itu.

  2. Tetapi karena risiko memisahkan nama yang Anda berikan ke variabel dengan nama parameter.

    Contoh:

    Katakanlah kode aslinya adalah:

    void doSomething(bool preventUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code:
    var preventUndo = true;
    doSomething(preventUndo);
    

    Kemudian, pengembang yang mengerjakan doSomethingpemberitahuan bahwa baru-baru ini, undo history memperkenalkan dua metode baru:

    undoHistory.clearAll() { ... }
    undoHistory.disable() { ... }
    

    Sekarang, preventUndosepertinya tidak terlalu jelas. Apakah itu berarti bahwa hanya tindakan terakhir yang akan dicegah? Atau mungkin itu berarti bahwa pengguna tidak akan dapat menggunakan fitur undo lagi? Atau sejarah undo akan dihapus? Nama yang lebih jelas adalah:

    doSomething(bool hideLastUndo) { ... }
    doSomething(bool removeAllUndo) { ... }
    doSomething(bool disableUndoFeature) { ... }
    

    Jadi sekarang Anda memiliki:

    void doSomething(bool hideLastUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code, very error prone, while the signature of the method is clear:
    var preventUndo = true;
    doSomething(preventUndo);
    

Bagaimana dengan enum?

Beberapa orang lain menyarankan menggunakan enum. Sementara itu memecahkan masalah langsung, itu menciptakan masalah yang lebih besar. Mari lihat:

enum undoPrevention
{
    keepInHistory,
    prevent,
}

void doSomething(undoPrevention preventUndo)
{
    doTheJob();
    if (preventUndo == undoPrevention.prevent)
    {
        this.undoHistory.discardLastEntry();
    }
}

doSomething(undoPrevention.prevent);

Masalah dengan itu:

  1. Terlalu banyak kode. if (preventUndo == undoPrevention.prevent)? Serius ?! Saya tidak ingin menulis seperti ifitu setiap waktu.

  2. Menambahkan elemen ke enum sangat menggoda nanti, jika saya menggunakan enum yang sama di tempat lain. Bagaimana jika saya memodifikasinya seperti ini:

    enum undoPrevention
    {
        keepInHistory,
        prevent,
        keepButDisable, // Keeps the entry in the history, but makes it disabled.
    }
    

    Apa yang akan terjadi sekarang? Akankah doSomethingmetode bekerja seperti yang diharapkan? Untuk mencegahnya, Anda harus menulis metode ini dengan cara ini dari awal:

    void doSomething(undoPrevention preventUndo)
    {
        if (![undoPrevention.keepInHistory, undoPrevention.prevent].contains(preventUndo))
        {
            throw new ArgumentException('preventUndo');
        }
    
        doTheJob();
        if (preventUndo == undoPrevention.prevent)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    

    Varian yang menggunakan boolean mulai terlihat sangat bagus!

    void doSomething(bool preventUndo)
    {
        doTheJob();
        if (preventUndo)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    

3
"Terlalu banyak kode. If (preventUndo == undoPrevention.prevent)? Serius ?! Saya tidak ingin menulis seandainya setiap waktu.": Bagaimana dengan menggunakan pernyataan switch lama yang bagus? Juga, jika Anda menggunakan boolean, Anda tetap membutuhkan pernyataan if. Jujur saja, kritikmu tentang hal ini sepertinya sedikit dibuat-buat untukku.
Giorgio

1
Saya kehilangan solusi alternatif di sini. Apakah Anda mengatakan bahwa ia harus tetap berpegang pada ucapan tidak ada yang benar dan salah dan mengandalkan IDE (yang tidak ia gunakan)? Sebuah komentar dapat membusuk seperti nama variabel.
Aman

2
@superM: Saya tidak merasa jauh lebih sulit untuk mengubah nama variabel sementara yang hanya digunakan untuk panggilan. Jika nama enum diubah, maka itu lebih baik, karena kode panggilan tidak akan berjalan lagi dan melemparkan kesalahan langsung ke wajah Anda. Jika fungsi fungsi secara radikal diubah oleh ekstensi enum, maka Anda harus meninjau kembali semua panggilan untuk kebenaran lebih lanjut, lagi pula, jika Anda memprogram dengan harapan, bukan pada logika. Bahkan tidak memikirkan untuk mengubah nilai, misalnya meniadakan preventUndoke allowUndodalam tanda tangan ...
Aman

4
@superM menjaga komentar tetap sinkron dengan kode memang sangat rawan masalah, karena Anda tidak memiliki bantuan dari kompiler Anda dalam pencarian Anda untuk inkonsistensi.
Buhb

7
"... mengandalkan IDE Anda untuk memberi Anda tanda tangan dari metode yang disebut" - ini berguna untuk saat Anda menulis kode, tetapi tidak begitu banyak ketika Anda membacanya. Ketika saya membaca kode untuk sebuah kelas, saya ingin tahu apa yang dilakukannya hanya dengan melihatnya. Jika Anda harus bergantung pada IDE untuk keterbacaan, kode Anda tidak dapat didokumentasikan sendiri.
Phil

20

Anda juga tidak boleh menulis metode dengan bendera boolean.

Mengutip Robert C. Martin dalam bukunya Clean Code (ISBN-13 978-0-13-235088-4), "Melewati boolean menjadi fungsi adalah praktik yang benar-benar mengerikan."

Mengutipnya, alasannya adalah fakta bahwa Anda memiliki benar / salah beralih berarti bahwa metode Anda kemungkinan besar melakukan dua hal yang berbeda (yaitu "Melakukan sesuatu dengan membatalkan" dan "Melakukan sesuatu tanpa membatalkan"), dan karena itu harus dibagi menjadi dua metode yang berbeda (yang secara internal dapat menyebut hal yang sama).

DoSomething()
{...

DoSomethingUndoable()
{....

7
Dengan logika itu, konstruktor HotDog tunggal saya perlu sekitar 16 metode yang berbeda: HotDogWithMustardRelishKrautOnion (), HotDogWithMustardNorelishKrautOnion (), dan sebagainya. Atau, itu bisa berupa HotDogWithOptions sederhana (opsi int), di mana saya menafsirkan opsi sebagai bidang bit, tapi kemudian kita kembali ke satu metode yang melakukan beberapa hal yang diduga berbeda. Jadi jika saya menggunakan opsi pertama, apakah saya harus menulis persyaratan besar untuk memilah konstruktor mana yang harus dipanggil berdasarkan hal-hal yang seharusnya menjadi parameter Boolean? Dan jika Q menggunakan int, A ini tidak menjawab Q.
Caleb

4
Setelah membaca semua jawaban dan melihat kode saya, saya sampai pada kesimpulan bahwa ini adalah cara yang tepat untuk melakukannya. doSomethingUndoable()dapat menangkap perubahan dan kemudian memanggildoSomething()
methodofaction

1
Jadi jika saya memiliki fungsi yang melakukan banyak pemrosesan dan memiliki opsi untuk mengirim email ketika sudah selesai, saya seharusnya tidak memiliki argumen boolean yang dapat mencegah pengiriman email, saya harus membuat fungsi terpisah?
yakatz

2
@ Caleb Dalam hal ini saya akan membuat hotdog dan menambahkan bahan satu per satu, atau dalam bahasa yang sangat diketik saya akan melewati objek HotDogSettings di mana saya akan mengatur semua bendera boolean. Saya tidak akan memiliki 15 flag benar / salah yang berbeda yang akan menjadi mimpi buruk untuk dipertahankan. ingat bahwa kode adalah tentang pemeliharaan bukan penulisan karena 80% dari biaya aplikasi. var hd = MakeHotDog (); hd.Add (Onion); ...
Nick B.

2
@ Caleb Saya pikir perbedaannya adalah bahwa contoh Anda menggunakan boolean sebagai bidang data aktual, sementara sebagian besar kegunaannya adalah untuk mendorong aliran eksekusi; karenanya Paman Bob mencercanya. Juga, sarannya sedikit di sisi ekstrim - beberapa baris berikutnya dalam buku ini menyarankan agar tidak memiliki lebih dari 2 parameter, yang mungkin merupakan sikap yang bahkan lebih ekstrem. Ada metode untuk kegilaan itu. Anda benar bahwa itu tidak menjawab pertanyaan, kesalahan yang saya buat dalam jawaban saya sendiri.
Daniel B

5

Ketika Anda melihat dua kelas untuk melihat bagaimana mereka digabungkan, salah satu kategorinya adalah penggabungan data , yang merujuk pada kode dalam satu metode panggilan kelas dari kelas lain dan hanya meneruskan data, seperti bulan apa Anda ingin laporan, dan yang lain kategori adalah kopling kontrol , metode panggilan dan mengirimkan sesuatu yang mengontrol perilaku metode. Contoh yang saya gunakan di kelas adalah verbosebendera atau reportTypebendera, tetapi preventUndojuga merupakan contoh yang bagus. Seperti yang baru saja Anda tunjukkan, kontrol kopling membuatnya sulit untuk membaca kode panggilan dan memahami apa yang terjadi. Ini juga membuat kode panggilan rentan terhadap perubahan doSomething()yang menggunakan bendera yang sama untuk mengontrol undo dan arsip, atau menambahkan parameter lain kedoSomething() untuk mengontrol pengarsipan, sehingga melanggar kode Anda, dan sebagainya.

Masalahnya adalah bahwa kodenya terlalu erat. Melewati bool untuk mengendalikan perilaku, menurut pendapat saya, merupakan pertanda API yang buruk. Jika Anda memiliki API, ubahlah. Dua metode, doSomething()dan doSomethingWithUndo(), akan lebih baik. jika Anda tidak memilikinya, tulis sendiri dua metode pembungkus dalam kode yang Anda miliki, dan mintalah salah satu dari mereka memanggil doSomething(true)dan panggilan lainnya doSomething(false).


4

Bukan peran penelepon untuk menentukan peran argumen. Saya selalu mencari versi inline, dan memeriksa tanda tangan dari fungsi yang dipanggil jika saya ragu.

Variabel harus dinamai sesuai perannya dalam lingkup saat ini. Bukan Ruang lingkup di mana mereka dikirim.


1
peran variabel dalam lingkup saat ini adalah untuk mengetahui untuk apa ia digunakan. Saya tidak melihat bagaimana ini bertentangan dengan penggunaan variabel sementara OP.
superM

4

Apa yang akan saya lakukan dalam JavaScript adalah memiliki fungsi mengambil objek sebagai satu-satunya parameter seperti ini:

function doSomething(settings) {
    var preventUndo = settings.hasOwnProperty('preventUndo') ? settings.preventUndo : false;
    // Deal with preventUndo in the normal way.
}

Kemudian sebut dengan:

doSomething({preventUndo: true});

HA!!! Kami berdua membuat doSomething(x)metode! lol ... Saya bahkan tidak melihat posting Anda sampai sekarang.
blesh

Apa yang akan terjadi jika saya melewati string "noUndo", dan bagaimana jelas bagi orang yang menggunakan tanda tangan Anda pengaturan apa yang seharusnya berisi tanpa melihat ke dalam implementasi?
Nick B.

1
Dokumentasikan kode Anda. Itulah yang dilakukan orang lain. Itu membuat membaca kode Anda lebih mudah, menulis itu hanya harus terjadi sekali.
ridecar2

1
@NickB. konvensi sangat kuat. Jika sebagian besar kode Anda menerima objek dengan argumen, ini jelas.
orip

4

Saya suka memanfaatkan fitur bahasa untuk membantu kejelasan diri saya. Misalnya dalam C # Anda dapat menentukan parameter dengan nama:

 CallSomething(name: "So and so", age: 12, description: "A random person.");

Dalam JavaScript, saya biasanya lebih suka menggunakan objek opsi sebagai argumen karena alasan ini:

function doSomething(args) { /*...*/ }

doSomething({ name: 'So and so', age: 12, description: 'A random person.' });

Itu hanya preferensi saya. Saya kira itu akan sangat tergantung pada metode yang dipanggil, dan bagaimana IDE membantu pengembang memahami tanda tangan.


1
Itulah "sisi buruk" dari bahasa yang diketik secara longgar. Anda tidak dapat benar-benar menegakkan tanda tangan tanpa validasi. Saya tidak tahu Anda bisa menambahkan nama seperti itu di JS.
James P.

1
@ JamesPoulson: Anda mungkin tahu Anda bisa melakukannya di JS dan tidak menyadarinya. Semuanya ada di JQuery: $.ajax({url:'', data:''})etc etc.
blesh

Benar. Tampaknya tidak biasa pada pandangan pertama tetapi mirip dengan objek semu sebaris yang dapat ada dalam beberapa bahasa.
James P.

3

Saya menemukan ini adalah yang paling sederhana dan paling mudah dibaca:

enum Undo { ALLOW, PREVENT }

doSomething(Undo u) {
    if (Undo.ALLOW == u) {
        // save stuff to undo later.
    }
    // do your thing
}

doSomething(Undo.ALLOW);

MainMa tidak suka itu. Kita mungkin harus setuju untuk tidak setuju, atau kita dapat menggunakan solusi yang diusulkan Joshua Bloch:

enum Undo {
    ALLOW {
        @Override
        public void createUndoBuffer() {
            // put undo code here
        }
    },
    PREVENT {
        @Override
        public void createUndoBuffer() {
            // do nothing
        }
    };

    public abstract void createUndoBuffer();
}

doSomething(Undo u) {
    u.createUndoBuffer();
    // do your thing
}

Sekarang jika Anda pernah menambahkan Undo.LIMITED_UNDO, kode Anda tidak akan dikompilasi kecuali jika Anda menerapkan metode createUndoBuffer (). Dan di doSomething () tidak ada jika (Undo.ALLOW == u). Saya telah melakukan keduanya dan metode kedua cukup berat dan sedikit sulit untuk dipahami ketika Undo enum meluas ke halaman dan halaman kode, tetapi itu membuat Anda berpikir. Saya biasanya tetap dengan metode yang lebih sederhana untuk mengganti boolean sederhana dengan enum 2-nilai sampai saya punya alasan untuk berubah. Ketika saya menambahkan nilai ketiga, saya menggunakan IDE saya untuk "Temukan penggunaan" dan perbaiki semuanya.


2

Saya pikir solusi Anda membuat kode Anda sedikit lebih mudah dibaca tetapi saya akan menghindari mendefinisikan variabel tambahan hanya untuk membuat panggilan fungsi Anda lebih jelas. Juga, jika Anda ingin menggunakan variabel tambahan, saya akan menandainya sebagai konstan jika bahasa pemrograman Anda mendukungnya.

Saya dapat memikirkan dua alternatif yang tidak melibatkan variabel tambahan:

1. Gunakan komentar ekstra

doSomething(/* preventUndo */ true);

2. Gunakan enum dua nilai, bukan boolean

enum Options
{
    PreventUndo,
    ...
}

...

doSomething(PreventUndo);

Anda dapat menggunakan alternatif 2 jika bahasa Anda mendukung enum.

SUNTING

Tentu saja, menggunakan argumen bernama juga merupakan opsi, jika bahasa Anda mendukungnya.

Mengenai pengkodean tambahan yang dibutuhkan oleh enums, sepertinya benar-benar diabaikan bagi saya. Dari pada

if (preventUndo)
{
    ...
}

kamu punya

if (undoOption == PreventUndo)
{
    ...
}

atau

switch (undoOption)
{
  case PreventUndo:
  ...
}

Dan bahkan jika itu sedikit lebih banyak mengetik, ingatlah bahwa kode ditulis sekali dan dibaca berulang kali, sehingga dapat bermanfaat mengetik lebih sedikit sekarang untuk menemukan kode yang lebih mudah dibaca enam bulan kemudian.


2

Saya setuju dengan apa yang dikatakan @GlenPeterson:

doSomething(Undo.ALLOW); // call using a self evident enum

tetapi juga insteresting akan menjadi berikut karena menurut saya hanya ada dua kemungkinan (benar atau salah)

//Two methods    

doSomething()  // this method does something but doesn't prevent undo

doSomethingPreventUndo() // this method does something and prevents undo

2
Ide bagus - saya belum memikirkan itu. Ketika Anda menjadi lebih rumit seperti doSomething (String, String, String, boolean, boolean, boolean) maka konstanta bernama adalah satu-satunya solusi yang masuk akal. Yah, Josh Bloch menyarankan objek pabrik dan konstruktor seperti di: MyThingMaker thingMaker = MyThingMaker.new (); thingMaker.setFirstString ("Hai"); thingMaker.setSecondString ("Ada"); dll ... Hal t = thingMaker.makeIt (); t.doSomething (); Itu hanya pekerjaan yang lebih banyak, jadi lebih baik sia-sia.
GlenPeterson

Salah satu masalah dengan pendekatan yang terakhir adalah bahwa itu membuatnya canggung untuk menulis metode yang menggunakan doSomethingtetapi memungkinkan penelepon pilihan apakah akan memperbolehkan undo. Obat mungkin memiliki kelebihan yang mengambil opsi Boolean, tetapi juga memiliki versi pembungkus yang memanggil versi sebelumnya dengan parameter selalu benar atau selalu salah.
supercat

@GlenPeterson Sebuah pabrik adalah suatu kemungkinan dan dimungkinkan dalam Javascript (disebutkan oleh OP).
James P.

2

Karena Anda bertanya tentang javascript terutama, dengan menggunakan coffeescript Anda dapat memiliki argumen bernama seperti sintaks, super mudah:

# declare method, ={} declares a default value to prevent null reference errors
doSomething = ({preventUndo} = {}) ->
  if preventUndo
    undo = no
  else
    undo = yes

#call method
doSomething preventUndo: yes
#or 
doSomething(preventUndo: yes)

kompilasi ke

var doSomething;

doSomething = function(_arg) {
  var preventUndo, undo;
  preventUndo = (_arg != null ? _arg : {}).preventUndo;
  if (preventUndo) {
    return undo = false;
  } else {
    return undo = true;
  }
};

doSomething({
  preventUndo: true
});

doSomething({
  preventUndo: true
});

Bersenang-senanglah di http://js2coffee.org/ untuk mencoba berbagai kemungkinan


Saya menggunakan coffeeScript hanya ketika saya harus menghindari kegilaan OOP javascript. Ini adalah solusi yang bagus yang mungkin saya gunakan ketika coding saja, akan sulit membenarkan rekan kerja bahwa saya melewati objek bukannya boolean demi keterbacaan!
methodofaction

1

Hanya untuk solusi yang kurang konvensional dan karena OP menyebutkan Javascript, kemungkinan adalah menggunakan array asosiatif untuk memetakan nilai. Keuntungan lebih dari satu boolean adalah bahwa Anda dapat memiliki argumen sebanyak yang Anda suka dan tidak khawatir tentang urutannya.

var doSomethingOptions = new Array();

doSomethingOptions["PREVENTUNDO"] = true;
doSomethingOptions["TESTMODE"] = false;

doSomething( doSomethingOptions );

// ...

function doSomething( doSomethingOptions ){
    // Check for null here
    if( doSomethingOptions["PREVENTUNDO"] ) // ...
}

Saya menyadari ini adalah refleks seseorang dari latar belakang yang sangat diketik dan mungkin tidak begitu praktis tetapi menganggap ini sebagai orisinalitas.

Kemungkinan lain adalah memiliki objek dan juga dimungkinkan dalam Javascript. Saya tidak 100% yakin ini benar secara sintaksis. Lihatlah pola-pola seperti Factory untuk inspirasi lebih lanjut.

function SomethingDoer( preventUndo ) {
    this.preventUndo = preventUndo;
    this.doSomething = function() {
            if( this.preventUndo ){
                    // ...
            }
    };
}

mySomethingDoer = new SomethingDoer(true).doSomething();

1
Saya pikir ini bisa lebih baik ditulis dalam notasi-titik ( doSomethingOptions.PREVENTUNDO) daripada notasi akses array.
Paŭlo Ebermann

1

Fokus pertanyaan Anda dan jawaban lainnya adalah meningkatkan keterbacaan di mana fungsi dipanggil, yang merupakan fokus yang saya setujui. Setiap pedoman khusus, acara "tidak ada argumen boolean", harus selalu berfungsi sebagai sarana untuk tujuan ini dan tidak menjadi dan mengakhiri sendiri.

Saya pikir ini berguna untuk mencatat bahwa masalah ini sebagian besar dihindari ketika bahasa pemrograman mendukung argumen bernama / argumen kata kunci, seperti dalam C # 4 dan Python, atau di mana argumen metode disisipkan dalam nama metode, seperti dalam Smalltalk atau Objective-C.

Contoh:

// C# 4
foo.doSomething(preventUndo: true);
# Python
foo.doSomething(preventUndo=True)
// Objective-C
[foo doSomethingWith:bar shouldPreventUndo:YES];

0

Orang harus menghindari melewati variabel boolean sebagai argumen untuk fungsi. Karena fungsi harus melakukan satu hal pada satu waktu. Dengan melewati variabel boolean, fungsi tersebut memiliki dua perilaku. Ini juga menciptakan masalah keterbacaan untuk pada tahap selanjutnya atau untuk programmer lain yang cenderung melihat kode Anda. Ini juga menciptakan masalah dalam menguji fungsi. Mungkin dalam kasus ini Anda harus membuat dua test case. Bayangkan jika Anda memiliki fungsi yang sudah mendapat pernyataan sakelar dan mengubah perilakunya berdasarkan jenis sakelar, maka Anda harus memiliki begitu banyak kasus uji yang berbeda yang ditentukan untuknya.

Setiap kali seseorang menemukan argumen boolean untuk fungsi tersebut, mereka harus mengubah kode sehingga mereka tidak menulis argumen boolean sama sekali.


Jadi, Anda akan mengonversi suatu fungsi dengan argumen boolean dalam dua fungsi yang hanya berbeda di sebagian kecil kode mereka (di mana yang asli akan memiliki if(param) {...} else {...})?
Paŭlo Ebermann

-1
int preventUndo = true;
doSomething(preventUndo);

Kompiler yang bagus untuk bahasa tingkat rendah seperti C ++ akan mengoptimalkan kode mesin di atas 2 baris seperti di bawah ini:

doSomething(true); 

jadi itu tidak masalah dalam kinerja tetapi akan meningkatkan keterbacaan.

Juga sangat membantu untuk menggunakan variabel dalam kasus itu akan menjadi variabel nanti dan dapat menerima nilai-nilai lain juga.

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.