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.
this
ke salah satu metode yang Anda panggil dari konstruktor.Jawaban:
Anda belum menentukan bahasa.
Dalam C ++ seorang konstruktor harus waspada 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).
sumber
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 adalah dengan memanggil metode virtual (langsung atau tidak langsung). Dan alasan utamanya adalah 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 Effecive Java 2nd Edition , Butir 17: Desain dan dokumen untuk warisan atau melarangnya .
Perhatikan bahwa ini adalah kasus khusus masalah umum penerbitan referensi objek sebelum waktunya . Metode instan memiliki
this
parameter implisit , tetapi meneruskanthis
secara eksplisit ke metode dapat menyebabkan masalah yang sama. 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.sumber
Saya tidak akan menganggap pemanggilan metode di sini sebagai antipattern dalam dirinya sendiri, lebih merupakan bau kode. Jika kelas menyediakan
reset
metode, yang mengembalikan objek ke keadaan semula, maka memanggilreset()
konstruktor adalah KERING. (Saya tidak membuat pernyataan apa pun tentang metode reset).Berikut ini adalah 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:
Pengujian unit melibatkan banyak penciptaan dan penghancuran - oleh karena itu konstruksi harus cepat.
Bergantung pada apa yang dilakukan oleh metode-metode tersebut, mungkin menyulitkan untuk menguji unit kode yang terpisah tanpa mengandalkan beberapa prakondisi (yang berpotensi tidak dapat diuji coba) yang diatur dalam konstruktor (mis. Dapatkan info dari jaringan).
sumber
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.
sumber
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 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 dukungan OOP waras, yang mengatur pointer vtable dengan benar dari awal, masalah ini tidak ada.
sumber
Ada dua masalah dengan memanggil metode:
Tidak ada yang salah dengan memanggil fungsi pembantu, selama itu tidak termasuk dalam dua kasus sebelumnya.
sumber
Saya tidak membeli ini. Dalam sistem berorientasi objek, memanggil metode adalah satu-satunya hal yang dapat Anda lakukan. Bahkan, itu kurang lebih definisi "berorientasi objek". Jadi, jika seorang konstruktor tidak dapat memanggil metode apa pun, lalu apa yang bisa dilakukannya?
sumber
Dalam teori OOP, seharusnya tidak masalah, tetapi dalam praktiknya, setiap bahasa pemrograman OOP 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".
sumber