it-swarm-id.com

Konstruktor umumnya tidak boleh memanggil metode

Saya menjelaskan kepada seorang kolega mengapa seorang konstruktor yang memanggil suatu metode bisa menjadi antipemernah.

contoh (dalam C berkarat saya ++)

class C {
public :
    C(int foo);
    void setFoo(int foo);
private:
    int foo;
}

C::C(int foo) {
    setFoo(foo);
}

void C::setFoo(int foo) {
    this->foo = foo
}

Saya ingin memotivasi fakta ini dengan lebih baik melalui kontribusi tambahan Anda. Jika Anda memiliki contoh, referensi buku, halaman blog, atau nama prinsip, mereka akan sangat disambut.

Sunting: Saya berbicara secara umum, tetapi kami sedang mengkode dengan python.

12
Stefano Borini

Anda belum menentukan bahasa.

Dalam C++ seorang konstruktor harus berhati-hati ketika memanggil fungsi virtual, karena fungsi sebenarnya yang dipanggil adalah implementasi kelas. Jika ini adalah metode virtual murni tanpa implementasi, ini akan menjadi pelanggaran akses.

Konstruktor dapat memanggil fungsi non-virtual.

Jika bahasa Anda adalah Java di mana fungsi umumnya virtual secara default, masuk akal bahwa Anda harus ekstra hati-hati.

C # tampaknya menangani situasi seperti yang Anda harapkan: Anda dapat memanggil metode virtual dalam konstruktor dan itu memanggil versi paling final. Jadi di C # bukan anti-pola.

Alasan umum untuk memanggil metode dari konstruktor adalah Anda memiliki beberapa konstruktor yang ingin memanggil metode "init" yang umum.

Perhatikan bahwa destruktor akan memiliki masalah yang sama dengan metode virtual, sehingga Anda tidak dapat memiliki metode "pembersihan" virtual yang berada di luar destruktor Anda dan mengharapkannya dipanggil oleh destructor kelas dasar.

Java dan C # tidak memiliki destruktor, mereka memiliki finalizer. Saya tidak tahu perilaku dengan Java.

C # tampaknya menangani pembersihan dengan benar terkait hal ini.

(Perhatikan bahwa walaupun Java dan C # memiliki pengumpulan sampah, itu hanya mengelola alokasi memori. Ada pembersihan lain yang perlu dilakukan destruktor Anda yang tidak melepaskan memori).

26
CashCow

OK, sekarang kebingungan tentang metode kelas vs metode contoh dihapus, saya bisa memberikan jawaban :-)

Masalahnya bukan dengan memanggil metode instance secara umum dari konstruktor; itu dengan memanggil metode virtual (langsung atau tidak langsung). Dan alasan utamanya adalah bahwa ketika berada di dalam konstruktor, objek belum sepenuhnya dibangun . Dan terutama bagian-bagian subkelasnya sama sekali tidak dibangun ketika konstruktor kelas dasar mengeksekusi. Jadi keadaan internalnya tidak konsisten dengan cara yang tergantung pada bahasa, dan ini dapat menyebabkan bug halus yang berbeda dalam bahasa yang berbeda.

C++ dan C # sudah dibahas oleh orang lain. Di Jawa, metode virtual dari tipe yang paling diturunkan akan dipanggil, namun tipe itu belum diinisialisasi. Jadi jika metode itu menggunakan bidang apa pun dari tipe turunan, bidang tersebut mungkin belum diinisialisasi dengan benar pada titik waktu tersebut. Masalah ini dibahas secara rinci dalam Efektif Java Edisi ke-2 , Butir 17: Desain dan dokumen untuk warisan atau jika tidak, larang.

Perhatikan bahwa ini adalah kasus khusus dari masalah umum penerbitan referensi objek sebelum waktunya . Metode instan memiliki parameter this implisit, tetapi meneruskan this secara eksplisit ke metode dapat menyebabkan masalah yang serupa. Terutama dalam program bersamaan di mana jika referensi objek diterbitkan sebelum waktunya ke utas lain, utas itu sudah bisa memanggil metode di atasnya sebelum konstruktor di utas pertama selesai.

18
Péter Török

Saya tidak akan menganggap pemanggilan metode di sini sebagai antipattern dalam dirinya sendiri, lebih merupakan bau kode. Jika suatu kelas menyediakan metode reset, yang mengembalikan objek ke keadaan semula, maka memanggil reset() dalam konstruktor adalah KERING. (Saya tidak membuat pernyataan apa pun tentang metode reset).

Inilah artikel yang dapat membantu memenuhi permintaan Anda akan otoritas: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

Ini bukan tentang memanggil metode, tetapi tentang konstruktor yang melakukan terlalu banyak. IMHO, memanggil metode dalam konstruktor adalah bau yang mungkin menunjukkan bahwa konstruktor terlalu berat.

Ini terkait dengan betapa mudahnya menguji kode Anda. Alasannya termasuk:

  1. Pengujian unit melibatkan banyak penciptaan dan penghancuran - oleh karena itu konstruksi harus cepat.

  2. Bergantung pada apa yang dilakukan metode-metode tersebut, mungkin sulit untuk menguji unit kode yang terpisah tanpa bergantung pada beberapa prasyarat (yang berpotensi tidak dapat diuji coba) yang disiapkan di konstruktor (mis. Dapatkan info dari jaringan).

7
Paul Butcher

Secara filosofis, tujuan konstruktor adalah mengubah potongan memori mentah menjadi sebuah instance. Ketika konstruktor sedang dieksekusi, objek belum ada, oleh karena itu memanggil metodenya adalah ide yang buruk. Anda mungkin tidak tahu apa yang mereka lakukan secara internal, dan mereka mungkin menganggap objek itu setidaknya ada (ya!) Ketika mereka dipanggil.

Secara teknis, mungkin tidak ada yang salah dengan itu, di C++ dan terutama di Python, terserah Anda untuk berhati-hati.

Secara praktis, Anda harus membatasi panggilan hanya pada metode yang menginisialisasi anggota kelas.

3
user17621

Ini bukan masalah tujuan umum. Ini masalah dalam C++, khususnya ketika menggunakan metode pewarisan dan virtual, karena konstruksi objek terjadi mundur, dan penunjuk vtable disetel ulang dengan setiap lapisan konstruktor dalam hierarki warisan, jadi jika Anda memanggil metode virtual yang Anda mungkin tidak akhirnya mendapatkan yang benar-benar sesuai dengan kelas yang Anda coba buat, yang mengalahkan seluruh tujuan menggunakan metode virtual.

Dalam bahasa dengan waras OOP dukungan, yang mengatur pointer vtable dengan benar dari awal, masalah ini tidak ada.

2
Mason Wheeler

Ada dua masalah dengan memanggil metode:

  • memanggil metode virtual, yang dapat melakukan sesuatu yang tidak terduga (C++) atau menggunakan bagian dari objek yang belum diinisialisasi
  • memanggil metode publik (yang seharusnya menegakkan invarian kelas), karena objek belum tentu lengkap (dan dengan demikian invariannya mungkin tidak berlaku)

Tidak ada yang salah dengan memanggil fungsi pembantu, selama itu tidak termasuk dalam dua kasus sebelumnya.

2
Matthieu M.

Saya tidak membeli ini. Dalam sistem berorientasi objek, memanggil metode adalah satu-satunya hal yang dapat Anda lakukan. Bahkan, itu kurang lebih definisi dari "berorientasi objek". Jadi, jika seorang konstruktor tidak dapat memanggil metode apa pun, lalu apa dapat itu lakukan?

1
Jörg W Mittag

Dalam O.O.P. teori, seharusnya tidak masalah, tetapi dalam praktiknya, setiap bahasa pemrograman O.O.P menangani konstruktor berbeda. Saya tidak sering menggunakan metode statis.

Dalam C++ & Delphi, Jika saya harus memberikan nilai awal ke beberapa properti ("anggota lapangan"), dan kode ini sangat diperluas, saya menambahkan beberapa metode sekunder sebagai ekstensi konstruktor.

Dan jangan memanggil metode lain yang melakukan hal-hal yang lebih kompleks.

Sedangkan untuk properti "getter" & "setter" metode, saya biasanya menggunakan variabel pribadi/dilindungi untuk menyimpan negara mereka, ditambah metode "getter" & "setter".

Dalam konstruktor saya menetapkan nilai "default" ke bidang status properti, TANPA memanggil "accessors".

0
umlcat