Saya masih menganggap diri saya sebagai programmer magang, jadi saya selalu mencari cara belajar yang "lebih baik" untuk pemrograman tipikal. Hari ini, rekan kerja saya berpendapat bahwa gaya pengkodean saya melakukan beberapa pekerjaan yang tidak perlu, dan saya ingin mendengar pendapat dari orang lain. Biasanya, ketika saya mendesain kelas dalam bahasa OOP (Biasanya C ++ atau Python), saya akan memisahkan inisialisasi menjadi dua bagian yang berbeda:
class MyClass1 {
public:
Myclass1(type1 arg1, type2 arg2, type3 arg3);
initMyClass1();
private:
type1 param1;
type2 param2;
type3 param3;
type4 anotherParam1;
};
// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
: param1(arg1)
, param2(arg2)
, param3(arg3)
{}
// Any other procedure is done in a separate initialization function
MyClass1::initMyClass1() {
// Validate input arguments before calculations
if (checkInputs()) {
// Do some calculations here to figure out the value of anotherParam1
anotherParam1 = someCalculation();
} else {
printf("Something went wrong!\n");
ASSERT(FALSE)
}
}
(atau, setara dengan python)
class MyClass1:
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
#optional
self.anotherParam1 = None
def initMyClass1():
if checkInputs():
anotherParam1 = someCalculation()
else:
raise "Something went wrong!"
Apa pendapat Anda tentang pendekatan ini? Haruskah saya menahan diri dari pemisahan proses inisialisasi? Pertanyaannya tidak hanya terbatas pada C ++ dan Python, dan jawaban untuk bahasa lain juga dihargai.
design
object-oriented
coding-style
Caladbolgll
sumber
sumber
Jawaban:
Meskipun terkadang bermasalah, ada banyak keuntungan untuk menginisialisasi segala sesuatu di konstruktor:
initMyClass1()
karena itu tidak ada . "Komponen termurah, tercepat, dan paling dapat diandalkan adalah komponen yang tidak ada di sana."sumber
Pikirkan abstraksi yang Anda berikan kepada pengguna Anda.
Mengapa membagi sesuatu yang bisa dilakukan dalam satu tembakan menjadi dua?
Inisialisasi tambahan hanyalah sesuatu yang ekstra untuk diingat oleh para programmer yang menggunakan API Anda, dan memberikan lebih banyak kesalahan jika mereka tidak melakukannya dengan benar, tetapi untuk nilai apa bagi mereka untuk beban tambahan ini?
Anda ingin memberikan abstraksi sederhana, mudah digunakan, sulit salah mati. Pemrograman cukup sulit tanpa hal-hal gratis untuk diingat / dilompati. Anda ingin pengguna API Anda (bahkan jika itu hanya Anda menggunakan API Anda sendiri) untuk jatuh ke dalam lubang kesuksesan .
sumber
Inisialisasi semuanya kecuali area data besar. Alat analisis statis akan menandai bidang yang tidak diinisialisasi dalam konstruktor. Namun, cara paling produktif / aman adalah memiliki semua variabel anggota dengan konstruktor default dan secara eksplisit menginisialisasi hanya yang memerlukan inisialisasi non-standar.
sumber
Ada kasus di mana objek memiliki banyak inisialisasi yang dapat dibagi menjadi dua kategori:
Atribut yang tidak dapat diubah atau tidak perlu diatur ulang.
Atribut yang mungkin perlu kembali ke nilai asli (atau nilai templatised) berdasarkan pada beberapa kondisi setelah memenuhi pekerjaan mereka, semacam soft reset. mis. koneksi dalam pool koneksi.
Di sini bagian kedua dari inisialisasi disimpan dalam fungsi yang terpisah, katakan InitialiseObject (), dapat dipanggil di ctor.
Fungsi yang sama dapat dipanggil nanti jika soft reset diperlukan, tanpa harus membuang dan membuat ulang objek.
sumber
Seperti yang orang lain katakan, umumnya ide yang baik untuk diinisialisasi dalam konstruktor.
Namun, ada alasan untuk itu tidak berlaku atau mungkin tidak berlaku dalam kasus tertentu.
Menangani kesalahan
Dalam banyak bahasa, satu-satunya cara untuk memberi sinyal kesalahan pada konstruktor adalah dengan menaikkan pengecualian.
Jika inisialisasi Anda memiliki peluang yang masuk akal untuk meningkatkan kesalahan misalnya melibatkan IO atau parameternya bisa menjadi input pengguna, maka satu-satunya mekanisme yang terbuka bagi Anda adalah mengajukan pengecualian. Dalam beberapa kasus, ini mungkin bukan yang Anda inginkan dan mungkin lebih masuk akal untuk memisahkan kode rawan kesalahan ke fungsi inisialisasi terpisah.
Mungkin contoh paling umum dari hal ini adalah dalam C ++ jika standar proyek / organisasi adalah untuk mematikan pengecualian.
Mesin negara
Ini adalah kasus di mana Anda memodelkan objek yang memiliki transisi keadaan eksplisit. Misalnya, file atau soket yang bisa dibuka dan ditutup.
Dalam hal ini, adalah umum untuk konstruksi objek (dan penghapusan) untuk hanya berurusan dengan atribut berorientasi memori (nama file, port dll). Maka akan ada fungsi untuk secara khusus mengelola transisi keadaan misalnya buka, tutup yang secara efektif menginisialisasi dan menghancurkan fungsi.
Keuntungannya adalah dalam penanganan kesalahan, seperti di atas tetapi juga mungkin ada kasus untuk memisahkan konstruksi dari inisialisasi (misalnya Anda membuat vektor file dan membukanya secara tidak sinkron).
Kerugiannya, seperti yang orang lain katakan, adalah bahwa Anda sekarang membebani manajemen negara bagian pada pengguna kelas Anda. Jika Anda dapat mengelola hanya dengan konstruksi maka Anda dapat, katakanlah, gunakan RAII untuk melakukan ini secara otomatis.
sumber