Saya sedang berdiskusi dengan rekan kerja saya tentang seberapa banyak pekerjaan yang dapat dilakukan oleh seorang konstruktor. Saya memiliki kelas, B yang secara internal membutuhkan objek lain A. Objek A adalah salah satu dari sedikit anggota yang perlu dilakukan oleh kelas B. Semua metode publiknya tergantung pada objek internal A. Informasi tentang objek A disimpan dalam DB, jadi saya mencoba memvalidasi dan mendapatkannya dengan mencarinya di DB di konstruktor. Rekan kerja saya menunjukkan bahwa konstruktor seharusnya tidak melakukan banyak pekerjaan selain menangkap parameter konstruktor. Karena semua metode publik akan gagal juga jika objek A tidak ditemukan menggunakan input ke konstruktor, saya berpendapat bahwa alih-alih membiarkan contoh dibuat dan kemudian gagal, sebenarnya lebih baik untuk membuang di awal konstruktor.
Apa yang dipikirkan orang lain? Saya menggunakan C # jika itu membuat perbedaan.
Membaca Apakah pernah ada alasan untuk melakukan semua pekerjaan objek dalam sebuah konstruktor? saya bertanya-tanya mengambil objek A dengan pergi ke DB adalah bagian dari "inisialisasi lain yang diperlukan untuk membuat objek siap digunakan" karena jika pengguna memberikan nilai yang salah kepada konstruktor, saya tidak akan dapat menggunakan metode publiknya.
Konstruktor harus instantiate bidang objek dan melakukan inisialisasi lain yang diperlukan untuk membuat objek siap digunakan. Ini umumnya berarti konstruktor kecil, tetapi ada skenario di mana ini akan menjadi sejumlah besar pekerjaan.
sumber
Jawaban:
Pertanyaan Anda terdiri dari dua bagian yang sepenuhnya terpisah:
Haruskah saya membuang pengecualian dari konstruktor, atau haruskah saya gagal metode?
Ini jelas penerapan prinsip Gagal-cepat . Membuat konstruktor gagal jauh lebih mudah untuk di-debug dibandingkan harus menemukan mengapa metode ini gagal. Misalnya, Anda mungkin mendapatkan instance sudah dibuat dari beberapa bagian lain dari kode dan Anda mendapatkan kesalahan saat memanggil metode. Apakah sudah jelas objek yang dibuat salah? Tidak.
Adapun masalah "bungkus panggilan di coba / tangkap". Pengecualian dimaksudkan untuk menjadi luar biasa . Jika Anda tahu beberapa kode akan melempar pengecualian, Anda tidak membungkus kode dalam try / catch, tetapi Anda memvalidasi parameter kode itu sebelum Anda mengeksekusi kode yang dapat melempar pengecualian. Pengecualian hanya dimaksudkan sebagai cara untuk memastikan sistem tidak masuk ke keadaan tidak valid. Jika Anda tahu beberapa parameter input dapat menyebabkan keadaan tidak valid, Anda memastikan parameter itu tidak pernah terjadi. Dengan cara ini, Anda hanya perlu mencoba / menangkap di tempat, di mana Anda dapat secara logis menangani pengecualian, yang biasanya pada batas sistem.
Dapatkah saya mengakses "bagian lain dari sistem, seperti DB" dari konstruktor.
Saya pikir ini bertentangan dengan prinsip paling tidak heran . Tidak banyak orang mengharapkan konstruktor mengakses DB. Jadi tidak, Anda tidak harus melakukan itu.
sumber
Ugh.
Konstruktor harus melakukan sesedikit mungkin - masalahnya adalah bahwa perilaku pengecualian pada konstruktor canggung. Sangat sedikit programmer yang tahu perilaku yang tepat (termasuk skenario pewarisan), dan memaksa pengguna Anda untuk mencoba / menangkap setiap instantiasi adalah ... menyakitkan paling-paling.
Ada dua bagian untuk ini, di konstruktor dan menggunakan konstruktor. Itu canggung di konstruktor karena Anda tidak bisa berbuat banyak di sana ketika pengecualian terjadi. Anda tidak dapat mengembalikan jenis yang berbeda. Anda tidak dapat mengembalikan nol. Anda pada dasarnya dapat memikirkan kembali pengecualian, mengembalikan objek yang rusak (konstruktor buruk), atau (kasus terbaik) mengganti beberapa bagian internal dengan default waras. Menggunakan konstruktor canggung karena Instansiasi Anda (dan Instansiasi dari semua tipe turunan!) Dapat melempar. Maka Anda tidak dapat menggunakannya dalam inisialisasi anggota. Anda akhirnya menggunakan pengecualian untuk logika untuk melihat "apakah kreasi saya berhasil?", Melampaui keterbacaan (dan kerapuhan) yang disebabkan oleh coba / tangkap di mana-mana.
Jika konstruktor Anda melakukan hal-hal non-sepele, atau hal-hal yang dapat diperkirakan gagal, pertimbangkan
Create
metode statis (dengan konstruktor non-publik). Ini jauh lebih bermanfaat, lebih mudah untuk debug, dan masih membuat Anda contoh yang dibangun sepenuhnya ketika berhasil.sumber
Connection
objek dapatCreateCommand
untuk Anda sehingga Anda tahu perintah terhubung dengan tepat.Konstruktor - keseimbangan kompleksitas metode memang masalah diskusi tentang stile yang baik. Cukup sering
Class() {}
digunakan konstruktor kosong , dengan metodeInit(..) {..}
untuk hal-hal yang lebih rumit. Di antara argumen yang harus dipertimbangkan:Class x = new Class(); x.Init(..);
berkali-kali, sebagian dalam Tes Unit. Namun, tidak begitu baik untuk membaca. Terkadang, saya menempatkan mereka dalam satu baris kode.sumber
init
metode ini adalah bahwa menangani kegagalan jauh lebih mudah. Secara khusus, nilai pengembalianinit
metode dapat menunjukkan apakah inisialisasi berhasil, dan metode dapat memastikan objek selalu dalam keadaan baik.