Parameter opsional atau konstruktor kelebihan beban

28

Saya menerapkan a DelegateCommand, dan ketika saya hendak mengimplementasikan konstruktor, saya menemukan dua pilihan desain berikut:

1: Memiliki banyak konstruktor yang kelebihan beban

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2: Hanya memiliki satu konstruktor dengan parameter opsional

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

Saya tidak tahu mana yang harus digunakan karena saya tidak tahu apa keuntungan / kerugian yang mungkin datang dengan salah satu dari dua cara yang diusulkan. Keduanya bisa disebut seperti ini:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

Dapatkah seseorang tolong tunjukkan saya ke arah yang benar dan berikan umpan balik?

Thomas Flinkow
sumber
4
KISS dan YAGNI. Jika ragu, terapkan keduanya. Setelah beberapa saat, lakukan pencarian referensi ke kedua konstruktor. Tidak akan mengejutkan saya jika salah satu dari mereka tidak pernah dikonsumsi secara eksternal. Atau dikonsumsi secara marjinal. Ini adalah sesuatu yang mudah ditemukan melakukan analisis statis terhadap kode.
Laiv
1
Konstruktor statis (seperti Bitmap.FromFile) juga merupakan pilihan
BlueRaja - Danny Pflughoeft
3
Simpan aturan analisis kode CA1026: Parameter default tidak boleh digunakan dalam pikiran.
Dan Lyons
2
@Laiv apakah saya melewatkan sesuatu? Mereka digunakan dengan cara yang sama dan karenanya memiliki tanda tangan yang sama. Anda tidak dapat mencari referensi dan tahu yang dipikirkan si penelepon. Kedengarannya lebih seperti Anda mengacu pada apakah atau tidak OP harus memiliki argumen kedua, tapi bukan itu yang ditanyakan OP (dan mereka mungkin tahu pasti bahwa mereka membutuhkan keduanya kadang-kadang, maka mereka bertanya).
Kat
2
@ Vooo Compiler untuk Openedge | Progress 10.2B mengabaikannya. Cukup banyak membunuh strategi perubahan metode kami yang tidak melanggar; alih-alih, kami perlu menggunakan kelebihan beban untuk efek yang sama.
Bret

Jawaban:

24

Saya lebih suka beberapa konstruktor daripada nilai default dan secara pribadi saya tidak suka dua contoh konstruktor Anda, itu harus diimplementasikan secara berbeda.

Alasan untuk menggunakan beberapa konstruktor adalah bahwa yang utama hanya dapat memeriksa apakah semua parameter tidak nol dan apakah mereka valid sedangkan konstruktor lain dapat memberikan nilai default untuk yang utama.

Namun dalam contoh Anda, tidak ada perbedaan di antara mereka karena bahkan konstruktor sekunder meneruskan nullnilai default dan konstruktor primer harus mengetahui nilai default juga. Saya pikir seharusnya tidak.

Ini berarti akan lebih bersih dan lebih baik dipisahkan jika diterapkan dengan cara ini:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

perhatikan yang _ => truediteruskan ke konstruktor utama yang sekarang juga memeriksa semua parameter untuk nulldan tidak peduli dengan default.


Namun poin yang paling penting adalah ekstensibilitas. Beberapa konstruktor lebih aman ketika ada kemungkinan Anda akan memperluas kode Anda di masa depan. Jika Anda menambahkan lebih banyak parameter yang diperlukan, dan yang opsional harus muncul di akhir, Anda akan merusak semua implementasi Anda saat ini. Anda dapat membuat konstruktor lama [Obsolete]dan memberi tahu pengguna bahwa itu akan dihapus memberi mereka waktu untuk bermigrasi ke implementasi baru tanpa langsung memecahkan kode mereka.


Di sisi lain, membuat terlalu banyak parameter opsional akan membingungkan juga karena jika beberapa di antaranya diperlukan dalam satu skenario dan opsional di skenario lain, Anda perlu mempelajari dokumentasi alih-alih hanya memilih konstruktor yang tepat dengan hanya melihat parameternya.

t3chb0t
sumber
12
Di sisi lain, membuat terlalu banyak parameter opsional akan membingungkan ... Sejujurnya, memiliki terlalu banyak parameter (terlepas apakah itu opsional atau tidak) membingungkan.
Andy
1
@ DavidvidPacker Saya setuju dengan Anda, tetapi berapa banyak parameter terlalu banyak adalah cerita lain, saya pikir ;-)
t3chb0t
2
Ada perbedaan lain antara memiliki konstruktor yang menghilangkan parameter versus konstruktor yang memiliki default untuk parameter tersebut. Dalam kasus pertama, penelepon dapat secara eksplisit menghindari melewati parameter, sedangkan dalam kasus terakhir penelepon tidak dapat - karena melewatkan tidak ada parameter sama dengan melewati nilai default. Jika konstruktor perlu membuat perbedaan ini, maka dua konstruktor adalah satu-satunya cara untuk pergi.
Gary McGill
1
Sebagai contoh, misalkan saya memiliki kelas yang memformat string, yang secara opsional dapat saya buat dengan sebuah CultureInfoobjek. Saya lebih suka API yang memungkinkan CultureInfoparameter yang akan dipasok melalui parameter tambahan, tetapi bersikeras bahwa jika itu diberikan maka harus tidak menjadi null. Dengan begitu nullnilai-nilai tidak disengaja tidak disalahartikan sebagai makna "tidak ada".
Gary McGill
Saya tidak yakin ekstensibilitas benar-benar merupakan alasan terhadap parameter opsional; jika Anda memiliki parameter yang diperlukan sama sekali dan pernah mengubah jumlahnya, Anda harus membuat konstruktor baru dan Obsoleteyang lama jika Anda ingin menghindari kerusakan, apakah Anda juga memiliki parameter opsional atau tidak. Dengan parameter opsional, Anda hanya perlu melakukannya Obsoletejika menghapusnya.
Herohtar
17

Mengingat satu-satunya hal yang Anda lakukan di konstruktor adalah penugasan sederhana, solusi konstruktor tunggal mungkin merupakan pilihan yang lebih baik dalam kasus Anda. Konstruktor lain tidak menyediakan fungsionalitas tambahan dan jelas dari desain konstruktor dengan dua parameter bahwa argumen kedua tidak perlu disediakan.

Banyak konstruktor masuk akal ketika Anda membangun objek dari tipe yang berbeda. Dalam hal itu, konstruktor adalah pengganti pabrik eksternal karena mereka memproses parameter input dan memformatnya ke representasi properti internal yang benar dari kelas yang sedang dibangun. Namun itu tidak terjadi dalam kasus Anda karenanya mengapa satu konstruktor harus lebih dari cukup.

Andy
sumber
3

Saya pikir ada dua kasus berbeda di mana setiap pendekatan dapat bersinar.

Pertama, ketika kita memiliki konstruktor sederhana (yang biasanya terjadi, menurut pengalaman saya), saya akan mempertimbangkan argumen opsional untuk bersinar.

  1. Mereka meminimalkan kode yang harus ditulis (dan dengan demikian juga yang harus dibaca).
  2. Anda dapat memastikan bahwa dokumentasi ada di satu tempat dan tidak diulangi (yang buruk karena membuka area tambahan yang bisa usang).
  3. Jika Anda memiliki banyak argumen opsional, Anda dapat menghindari serangkaian kombinasi konstruktor yang sangat membingungkan. Heck, bahkan dengan hanya 2 argumen opsional (tidak terkait satu sama lain), jika Anda ingin konstruktor yang terpisah dan kelebihan beban, Anda harus memiliki 4 konstruktor (versi tanpa, versi dengan masing-masing, dan versi dengan keduanya). Ini jelas tidak skala dengan baik.

Namun, ada kasus di mana argumen opsional dalam konstruktor hanya membuat hal-hal yang jelas membingungkan. Contoh yang jelas adalah ketika argumen opsional ini bersifat eksklusif (yaitu, tidak dapat digunakan bersama). Memiliki konstruktor yang kelebihan beban yang hanya mengizinkan kombinasi argumen yang benar-benar valid akan memastikan kompilasi waktu pelaksanaan kendala ini. Yang mengatakan, Anda juga harus menghindari bahkan menghadapi kasus ini (misalnya, dengan beberapa kelas yang diwarisi dari basis, di mana setiap kelas melakukan perilaku eksklusif).

Kat
sumber
+1 untuk menyebutkan ledakan permutasi dengan beberapa parameter opsional.
Jpsy