Apa praktik terbaik untuk validasi parameter konstruktor?
Misalkan sedikit C #:
public class MyClass
{
public MyClass(string text)
{
if (String.IsNullOrEmpty(text))
throw new ArgumentException("Text cannot be empty");
// continue with normal construction
}
}
Apakah bisa diterima untuk melemparkan pengecualian?
Alternatif yang saya temui adalah pra-validasi, sebelum membuat contoh:
public class CallingClass
{
public MyClass MakeMyClass(string text)
{
if (String.IsNullOrEmpty(text))
{
MessageBox.Show("Text cannot be empty");
return null;
}
else
{
return new MyClass(text);
}
}
}
c#
validation
constructors
MPelletier
sumber
sumber
Jawaban:
Saya cenderung melakukan semua validasi saya di konstruktor. Ini adalah suatu keharusan karena saya hampir selalu membuat objek yang tidak berubah. Untuk kasus spesifik Anda, saya pikir ini dapat diterima.
Jika Anda menggunakan .NET 4 Anda dapat melakukan ini. Tentu saja ini tergantung pada apakah Anda menganggap string yang hanya berisi ruang putih tidak valid.
sumber
Init
untuk menerima semacam kode kesalahan ketika pengecualian akan berfungsi juga dan memaksa objek Anda untuk selalu mempertahankan status yang valid?ArgumentNullException
dan lainnya yang menguji kosong dan melemparArgumentException
. Memungkinkan penelepon lebih pilih-pilih jika mereka ingin dalam penanganan pengecualiannya.Banyak orang menyatakan bahwa konstruktor tidak boleh melempar pengecualian. KyleG di halaman ini , misalnya, melakukan hal itu. Jujur, saya tidak bisa memikirkan alasan mengapa tidak.
Dalam C ++, melempar pengecualian dari konstruktor adalah ide yang buruk, karena meninggalkan Anda dengan memori yang dialokasikan yang berisi objek tidak diinisialisasi yang Anda tidak punya referensi (mis. Itu adalah kebocoran memori klasik). Mungkin dari situlah stigma tersebut berasal - sekelompok pengembang C ++ sekolah lama setengah-setengah mengembangkan pembelajaran C # mereka dan hanya menerapkan apa yang mereka ketahui dari C ++ ke dalamnya. Sebaliknya, di Objective-C Apple memisahkan langkah alokasi dari langkah inisialisasi, sehingga konstruktor dalam bahasa itu dapat membuang pengecualian.
C # tidak dapat membocorkan memori dari panggilan yang gagal ke konstruktor. Bahkan beberapa kelas dalam kerangka NET. Akan melempar pengecualian di konstruktor mereka.
sumber
Melempar pengecualian IFF kelas tidak dapat dimasukkan ke dalam keadaan yang konsisten sehubungan dengan penggunaan semantiknya. Kalau tidak, jangan. JANGAN PERNAH membiarkan objek ada dalam keadaan tidak konsisten. Ini termasuk tidak menyediakan konstruktor lengkap (seperti memiliki konstruktor kosong + inisialisasi () sebelum objek Anda benar-benar dibangun) ... HANYA KATAKAN TIDAK!
Dalam keadaan darurat, semua orang melakukannya. Saya melakukannya kemarin untuk benda yang sangat sempit digunakan dalam lingkup yang ketat. Suatu hari nanti, saya atau orang lain mungkin akan membayar harga untuk slip itu dalam praktik.
Saya harus mencatat bahwa dengan "konstruktor" maksud saya hal yang klien panggil untuk membangun objek. Itu bisa dengan mudah menjadi sesuatu selain konstruk yang sebenarnya dengan nama "Konstruktor". Misalnya, sesuatu seperti ini di C ++ tidak akan melanggar prinsip IMNSHO:
Karena satu-satunya cara untuk membuat
funky_object
adalah dengan memanggilbuild_funky
prinsip tidak pernah membiarkan objek yang tidak valid tetap ada meskipun "Pembuat" yang sebenarnya tidak menyelesaikan pekerjaan.Itu banyak pekerjaan ekstra meskipun untuk keuntungan yang dipertanyakan (bahkan mungkin kerugian). Saya masih lebih suka rute pengecualian.
sumber
Dalam hal ini, saya akan menggunakan metode pabrik. Pada dasarnya, atur kelas Anda hanya memiliki konstruktor pribadi dan memiliki metode pabrik yang mengembalikan turunan objek Anda. Jika parameter awal tidak valid, kembalikan saja nol dan minta kode panggilan memutuskan apa yang harus dilakukan.
Jangan pernah melempar pengecualian kecuali kondisinya luar biasa. Saya berpikir bahwa dalam kasus ini, nilai kosong dapat diteruskan dengan mudah. Jika itu masalahnya, menggunakan pola ini akan menghindari pengecualian dan hukuman kinerja yang mereka tanggung sambil menjaga agar status MyClass tetap berlaku.
sumber
Seseorang dapat dengan wajar mempertanyakan pedoman ini dan berkata, "Tapi saya tidak mengikuti aturan ini, dan kode saya berfungsi dengan baik!" Untuk itu, saya akan menjawab, "Yah, itu mungkin benar, sampai tidak."
sumber
Itu tergantung apa yang dilakukan MyClass. Jika MyClass sebenarnya adalah kelas repositori data, dan teks parameter adalah string koneksi, maka praktik terbaik adalah melempar ArgumentException. Namun, jika MyClass adalah kelas StringBuilder (misalnya), maka Anda bisa membiarkannya kosong.
Jadi itu tergantung pada seberapa penting parameter teks untuk metode - apakah objek masuk akal dengan nilai nol atau kosong?
sumber
Preferensi saya adalah menetapkan default, tetapi saya tahu Java memiliki pustaka "Apache Commons" yang melakukan hal seperti ini, dan saya pikir itu ide yang cukup bagus juga. Saya tidak melihat masalah dengan melempar pengecualian jika nilai yang tidak valid akan menempatkan objek dalam keadaan tidak dapat digunakan; string bukan contoh yang baik tetapi bagaimana jika itu untuk DI orang miskin? Anda tidak dapat beroperasi jika nilai
null
dilewatkan di tempat, katakanlah,ICustomerRepository
antarmuka. Dalam situasi seperti ini, melemparkan pengecualian adalah cara yang benar dalam menangani berbagai hal, menurut saya.sumber
Ketika saya dulu bekerja di c ++, saya tidak menggunakan untuk melempar pengecualian pada konstruktor karena masalah kebocoran memori yang dapat menyebabkannya, saya telah mempelajarinya dengan cara yang sulit. Siapa pun yang bekerja dengan c ++ tahu betapa sulit dan bocornya memori bermasalah.
Tetapi jika Anda berada di c # / Java, maka Anda tidak memiliki masalah ini, karena pemulung akan mengumpulkan memori. Jika Anda menggunakan C #, saya pikir lebih baik menggunakan properti untuk cara yang bagus dan konsisten untuk memastikan bahwa batasan data dijamin.
sumber
Melempar pengecualian akan sangat menyusahkan bagi pengguna kelas Anda. Jika validasi adalah masalah maka saya biasanya membuat membuat objek proses 2 langkah.
1 - Buat instance
2 - Panggil metode Inisialisasi dengan hasil kembali.
ATAU
Panggil metode / fungsi yang menciptakan kelas untuk Anda yang dapat melakukan validasi.
ATAU
Memiliki bendera isValid, yang tidak terlalu saya sukai tetapi beberapa orang melakukannya.
sumber
isValid = false
. Harus menguji setelah kelas dibuat jika valid itu mengerikan, desain sangat rawan kesalahan. Dan, Anda bilang, punya konstruktor, lalu panggil inisialisasi ... Bagaimana jika saya tidak memanggil inisialisasi? Lalu bagaimana? Dan bagaimana jika Inisialisasi mendapatkan data yang buruk, dapatkah saya melemparkan pengecualian sekarang? Kelas Anda seharusnya tidak sulit untuk digunakan.