OOP Coding style: menginisialisasi semuanya pada konstruktor?


14

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.





Mengapa Anda biasanya melakukannya? Kebiasaan? Apakah Anda pernah diberi alasan untuk melakukannya?
JeffO

@ Jeffe Saya mendapatkan kebiasaan ini ketika saya bekerja untuk membuat GUI dengan perpustakaan MFC. Sebagian besar kelas terkait UI, seperti CApp, CWindow, CDlg, dan sebagainya, memiliki fungsi OnInit () yang dapat Anda timpa, yang merespons pesan balasannya.
Caladbolgll

Jawaban:


28

Meskipun terkadang bermasalah, ada banyak keuntungan untuk menginisialisasi segala sesuatu di konstruktor:

  1. Jika ada kesalahan, itu terjadi secepat mungkin dan paling mudah didiagnosis. Misalnya, jika nol adalah nilai argumen yang tidak valid, uji dan gagal dalam konstruktor.
  2. Objek selalu dalam keadaan valid. Seorang rekan kerja tidak dapat membuat kesalahan dan lupa menelepon initMyClass1()karena itu tidak ada . "Komponen termurah, tercepat, dan paling dapat diandalkan adalah komponen yang tidak ada di sana."
  3. Jika masuk akal, objek dapat dibuat kekal yang memiliki banyak keuntungan.

2

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 .


1

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.


0

Ada kasus di mana objek memiliki banyak inisialisasi yang dapat dibagi menjadi dua kategori:

  1. Atribut yang tidak dapat diubah atau tidak perlu diatur ulang.

  2. 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.


0

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.

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.