Apakah metode init () bau kode?

20

Apakah ada tujuan untuk mendeklarasikan init()metode untuk suatu tipe?

Saya tidak bertanya apakah kita harus memilih init()daripada konstruktor atau bagaimana menghindari menyatakaninit() .

Aku bertanya apakah ada setiap alasan di balik mendeklarasikan init()metode (melihat bagaimana umum itu) atau jika itu adalah kode bau dan harus dihindari.


The init()idiom cukup umum, tapi saya belum melihat manfaat nyata.

Saya berbicara tentang tipe yang mendorong inisialisasi melalui metode:

class Demo {
    public void init() {
        //...
    }
}

Kapan ini bisa digunakan dalam kode produksi?


Saya merasa itu mungkin bau kode karena itu menyarankan konstruktor tidak sepenuhnya menginisialisasi objek, menghasilkan objek yang dibuat sebagian. Objek seharusnya tidak ada jika statusnya tidak disetel.

Ini membuat saya percaya itu mungkin bagian dari semacam teknik yang digunakan untuk mempercepat produksi, dalam arti aplikasi perusahaan. Ini adalah satu-satunya alasan logis saya bisa memikirkan memiliki idiom seperti itu, saya hanya tidak yakin bagaimana itu akan bermanfaat jika demikian.

Vince Emigh
sumber
1
Untuk "... melihat seberapa umum itu ...": apakah itu umum? Bisakah Anda memberikan beberapa contoh? Mungkin Anda sedang berhadapan dengan kerangka kerja yang membutuhkan pemisahan inisialisasi dan konstruksi.
DATANG DARI
Apakah metode ini ditemukan pada kelas dasar atau kelas turunan, atau keduanya? (Atau: apakah metode yang ditemukan pada kelas yang termasuk dalam hierarki warisan? Apakah kelas dasar memanggil init()kelas turunan, atau sebaliknya?) Jika demikian, ini adalah contoh membiarkan kelas dasar menjalankan "post-constructor "Yang hanya dapat dieksekusi setelah kelas yang paling diturunkan menyelesaikan konstruksi. Ini adalah contoh inisialisasi multi-fase.
rwong
Jika Anda tidak ingin menginisialisasi pada titik instantiasi, masuk akal untuk memisahkan keduanya.
JᴀʏMᴇᴇ

Jawaban:

39

Ya, ini bau kode. Bau kode bukan sesuatu yang selalu perlu dihapus. Itu adalah sesuatu yang membuat Anda melihat kedua.

Di sini Anda memiliki objek di dua negara yang berbeda secara mendasar: pra-init dan pasca-init. Negara-negara tersebut memiliki tanggung jawab yang berbeda, metode berbeda yang diizinkan untuk dipanggil, dan perilaku yang berbeda. Secara efektif dua kelas berbeda.

Jika Anda secara fisik membuat mereka dua kelas yang terpisah, Anda akan secara statis menghapus seluruh kelas bug potensial, dengan biaya mungkin membuat model Anda tidak cocok dengan "model dunia nyata" sedekat mungkin. Anda biasanya nama yang pertama Configatau Setupatau sesuatu seperti itu.

Jadi lain kali, coba refactoring idiom konstruk Anda menjadi model dua kelas dan lihat bagaimana hasilnya untuk Anda.

Karl Bielefeldt
sumber
6
Saran Anda untuk mencoba model dua kelas itu bagus. Mengusulkan langkah konkret untuk mengatasi bau kode berguna.
Ivan
1
Ini adalah jawaban terbaik sejauh ini. Satu hal yang mengganggu saya: " Negara-negara bagian itu memiliki tanggung jawab yang berbeda, metode berbeda yang diizinkan untuk dipanggil, dan perilaku yang berbeda " - Tidak memisahkan tanggung jawab ini melanggar SRP. Jika tujuan dari perangkat lunak adalah untuk mereplikasi skenario dunia nyata di setiap aspek, ini masuk akal. Tetapi dalam produksi, devs kebanyakan menulis kode yang mendorong kemudahan pengelolaan, merevisi model jika diperlukan agar lebih sesuai dengan lingkungan berbasis perangkat lunak (lanjutan pada komentar berikutnya)
Vince Emigh
1
Dunia nyata adalah lingkungan yang berbeda. Program yang mencoba mereplikasinya tampaknya spesifik domain, karena Anda tidak akan / tidak seharusnya memperhitungkannya di sebagian besar proyek. Banyak hal yang disukai diterima ketika datang ke proyek domain tertentu, jadi saya mencoba untuk menjaga ini sebagai umum mungkin (seperti bagaimana memanggil thiskonstruktor adalah bau kode dan dapat mengakibatkan kode rawan kesalahan, dan menghindarinya dianjurkan terlepas dari domain apa proyek Anda berada).
Vince Emigh
14

Tergantung.

Sebuah initmetode adalah kode bau bila tidak perlu memiliki inisialisasi objek dipisahkan dari konstruktor. Terkadang ada kasus di mana masuk akal untuk memisahkan langkah-langkah ini.

Pencarian Google cepat memberi saya contoh ini . Saya dapat dengan mudah membayangkan lebih banyak kasus di mana kode dieksekusi selama alokasi objek (konstruktor) mungkin lebih baik dipisahkan dari inisialisasi itu sendiri. Mungkin Anda memiliki sistem leveled, dan alokasi / konstruksi terjadi di level X, tetapi inisialisasi hanya di level Y, karena hanya Y yang dapat memberikan parameter yang diperlukan. Mungkin "init" mahal dan harus dijalankan hanya untuk subset dari objek yang dialokasikan, dan penentuan subset itu hanya dapat dilakukan di level Y. Atau Anda ingin menimpa metode "init" (virtual) dalam turunan kelas yang tidak dapat dilakukan dengan konstruktor. Mungkin level X memberi Anda objek yang dialokasikan dari pohon warisan, tetapi level Y tidak mengetahui derivasi beton, hanya tentang antarmuka umum (di manainit mungkin didefinisikan).

Tentu saja, menurut pengalaman saya, kasus-kasus ini hanya sebagian kecil dari kasus standar di mana semua inisialisasi dapat dilakukan secara langsung dalam konstruktor, dan setiap kali Anda melihat initmetode yang terpisah , mungkin ide yang baik untuk mempertanyakan perlunya.

Doc Brown
sumber
2
Jawaban dalam tautan itu mengatakan itu berguna untuk mengurangi jumlah pekerjaan dalam konstruktor, tetapi mendorong objek yang dibuat sebagian. Konstruktor yang lebih kecil dapat dicapai melalui dekomposisi, jadi bagi saya tampaknya jawaban menciptakan masalah baru (potensi untuk lupa memanggil semua metode inisialisasi yang diperlukan, meninggalkan objek yang rentan kesalahan) dan jatuh di bawah kategori kode bau
Vince Emigh
@VinceEmigh: ok, itu contoh pertama aku bisa menemukan di sini di platfform SE, mungkin bukan yang terbaik, tetapi ada yang kasus penggunaan yang sah untuk terpisah initmetode. Namun, setiap kali Anda melihat metode seperti itu, jangan ragu untuk mempertanyakan perlunya.
Doc Brown
Saya mempertanyakan / menantang setiap kasus penggunaan untuk itu, karena saya merasa tidak ada situasi di mana itu akan menjadi kebutuhan. Bagi saya, ini waktu pembuatan objek yang buruk, dan harus dihindari karena itu adalah kandidat untuk kesalahan yang dapat dihindari melalui desain yang tepat. Jika ada penggunaan init()metode yang tepat, saya yakin saya akan mendapat manfaat dari belajar tentang tujuannya. Maafkan ketidaktahuan saya, saya hanya heran dengan betapa sulitnya waktu saya menemukan penggunaan untuk itu, mencegah saya dari menganggapnya sesuatu yang harus dihindari
Vince Emigh
1
@VinceEmigh: ketika Anda tidak dapat memikirkan situasi seperti itu, Anda perlu bekerja pada imajinasi Anda ;-). Atau baca jawaban saya lagi, jangan kurangi hanya menjadi "alokasi". Atau bekerja dengan lebih banyak kerangka kerja dari vendor yang berbeda.
Doc Brown
1
@DocBrown Menggunakan imajinasi daripada praktik yang terbukti mengarah ke beberapa kode yang funky, seperti inisialisasi penjepit ganda: pintar, tetapi tidak efisien dan harus dihindari. Saya tahu vendor menggunakannya, tetapi itu tidak berarti penggunaan itu dibenarkan. Jika Anda merasakannya, beri tahu saya alasannya, karena itulah yang saya coba cari tahu. Anda kelihatan mati-matian karena memiliki beberapa tujuan bermanfaat, tetapi sepertinya Anda kesulitan memberikan contoh yang mendorong desain yang baik. Saya tahu dalam keadaan apa itu bisa digunakan, tetapi haruskah itu digunakan?
Vince Emigh
5

Pengalaman saya dipecah menjadi dua kelompok:

  1. Kode di mana init () sebenarnya diperlukan. Ini dapat terjadi ketika superclass atau kerangka kerja mencegah konstruktor kelas Anda dari mendapatkan semua dependensinya selama konstruksi.
  2. Kode tempat init () digunakan tetapi bisa dihindari.

Dalam pengalaman pribadi saya, saya telah melihat beberapa contoh (1) tetapi lebih banyak contoh (2). Akibatnya, saya biasanya menganggap init () adalah kode-bau, tetapi ini tidak selalu terjadi. Terkadang Anda tidak bisa menyiasatinya.

Saya telah menemukan menggunakan Pola Builder sering dapat membantu menghilangkan kebutuhan / keinginan untuk memiliki init ().

Ivan
sumber
1
Jika superclass atau framework tidak memungkinkan tipe untuk mendapatkan dependensi yang dibutuhkan melalui konstruktor, bagaimana cara menambahkan init()metode menyelesaikannya? The init()metode tersebut perlu parameter untuk menerima dependensi, atau Anda harus instantiate dependensi dalam satu init()metode, yang juga bisa dilakukan dengan konstruktor. Bisakah Anda memberi contoh?
Vince Emigh
1
@VinceEmigh: init () terkadang dapat digunakan untuk memuat file konfigurasi dari sumber eksternal, membuka koneksi database, atau semacamnya. Metode DoFn.initialize () (dari framework Crunch apache) digunakan dengan cara ini. Itu juga dapat digunakan untuk memuat bidang internal non-serializable (DoFns harus serializable). Dua masalah di sini adalah bahwa (1) sesuatu perlu memastikan metode inisialisasi dipanggil dan (2) objek perlu tahu di mana ia akan mendapatkan (atau bagaimana ia akan membangun) sumber daya tersebut.
Ivan
1

Skenario khas ketika metode Init berguna adalah ketika Anda memiliki file konfigurasi yang ingin Anda ubah dan agar perubahan diperhitungkan tanpa memulai ulang aplikasi. Ini, tentu saja, tidak berarti bahwa metode Init harus dipanggil secara terpisah dari konstruktor. Anda bisa memanggil metode Init dari konstruktor, dan kemudian memanggilnya nanti ketika / jika parameter konfigurasi berubah.

Singkatnya: seperti untuk sebagian besar dilema di luar sana, apakah ini bau kode atau tidak, tergantung pada situasi dan keadaan.

Vladimir Stokic
sumber
Jika update konfigurasi, dan ini membutuhkan objek untuk me-reset / mengubah keadaan itu berdasarkan konfigurasi, apakah Anda tidak berpikir itu akan lebih baik untuk memiliki tindakan objek sebagai pengamat terhadap Config?
Vince Emigh
@Vince Emigh Tidak perlu. Pengamat akan berfungsi jika saya tahu saat yang tepat ketika konfigurasi berubah. Namun, jika data konfigurasi disimpan dalam file yang dapat diubah di luar aplikasi, maka tidak ada pendekatan yang benar-benar ellegant. Misalnya, jika saya memiliki program yang mem-parsing beberapa file dan mengubah data menjadi beberapa model internal, dan file konfigurasi terpisah berisi nilai default untuk data yang hilang, jika saya mengubah nilai default, saya akan membacanya lagi ketika saya menjalankan berikutnya parsing. Memiliki metode Init dalam aplikasi saya akan sangat berguna dalam kasus itu.
Vladimir Stokic
Jika file konfigurasi eksternal dimodifikasi saat runtime, tidak ada cara untuk reload variabel-variabel tanpa beberapa jenis pemberitahuan menginformasikan aplikasi Anda yang perlu memanggil init ( update/ reloadmungkin akan lebih deskriptif untuk perilaku semacam ini) untuk benar-benar mendaftar perubahan tersebut . Dalam hal itu, pemberitahuan itu akan menyebabkan nilai-nilai konfigurasi berubah di aplikasi Anda secara internal, yang saya yakin dapat diamati dengan membuat konfigurasi Anda dapat diamati, memberi tahu pengamat ketika konfigurasi diminta untuk mengubah salah satu dari nilai-nilai itu. Atau apakah saya salah memahami contoh Anda?
Vince Emigh
Sebuah aplikasi mengambil beberapa file eksternal (diekspor dari beberapa GIS, atau sistem lain), mem-parsing file-file itu dan mengubahnya menjadi beberapa model internal bahwa sistem yang aplikasi itu adalah bagian dari penggunaan. Selama proses itu, beberapa kesenjangan data dapat ditemukan dan kesenjangan data tersebut dapat diisi dengan menetapkan nilai. Nilai-nilai default tersebut dapat disimpan dalam file konfigurasi yang dapat dibaca pada awal proses transformasi, yang dipanggil setiap kali ada file baru untuk diubah. Di situlah metode Init mungkin berguna.
Vladimir Stokic
1

Tergantung bagaimana Anda menggunakannya.

Saya menggunakan pola itu dalam bahasa yang dikumpulkan sampah seperti Java / C # ketika saya tidak ingin terus merealokasi objek di heap (seperti ketika saya membuat video game dan perlu menjaga kinerja tinggi, pengumpul sampah membunuh kinerja). Saya menggunakan konstruktor untuk membuat alokasi tumpukan lain yang dibutuhkan, dan inituntuk membuat keadaan dasar yang berguna tepat sebelum setiap kali saya ingin menggunakannya kembali. Ini terkait dengan konsep kolam objek.

Ini juga berguna jika Anda memiliki beberapa konstruktor yang berbagi subset umum dari instruksi inisialisasi, tetapi dalam kasus itu initakan bersifat pribadi. Dengan begitu saya dapat meminimalkan setiap konstruktor sebanyak mungkin, sehingga masing-masing hanya berisi instruksi unik dan satu panggilan inituntuk melakukan sisanya.

Secara umum, ini adalah bau kode.

Cody
sumber
Bukankah reset()metode akan lebih deskriptif untuk pernyataan pertama Anda? Adapun yang kedua (banyak konstruktor), memiliki banyak konstruktor adalah bau kode. Ini mengasumsikan objek memiliki beberapa tujuan / tanggung jawab, menunjukkan pelanggaran SRP. Objek harus memiliki satu tanggung jawab, dan konstruktor harus menentukan dependensi yang diperlukan untuk satu tanggung jawab itu. Jika Anda memiliki beberapa konstruktor karena nilai opsional, mereka harus menggunakan teleskop (yang juga merupakan bau kode, sebaiknya menggunakan pembangun).
Vince Emigh
@VinceEmigh Anda dapat menggunakan init atau reset, itu hanya nama. Init lebih masuk akal dalam konteks saya cenderung menggunakannya karena tidak masuk akal untuk mengatur ulang objek yang tidak pernah ditetapkan untuk pertama kali saya menggunakannya. Adapun masalah konstruktor, saya mencoba untuk menghindari memiliki banyak konstruktor, tetapi kadang-kadang sangat membantu. Lihatlah stringdaftar konstruktor bahasa apa pun , berton-ton opsi. Bagi saya biasanya mungkin 3 konstruktor maks, tetapi bagian umum dari instruksi sebagai inisialisasi masuk akal ketika mereka berbagi kode apa pun tetapi berbeda dalam hal apa pun.
Cody
Nama sangat penting. Mereka menggambarkan perilaku yang dilakukan tanpa memaksa klien untuk membaca kontrak. Penamaan yang buruk dapat menyebabkan asumsi yang salah. Adapun beberapa konstruktor dalam String, ini bisa diselesaikan dengan memisahkan pembuatan string. Pada akhirnya, a Stringadalah a String, dan konstruktornya hanya boleh menerima apa yang diperlukan agar dapat berfungsi sesuai kebutuhan. Sebagian besar konstruktor terbuka untuk tujuan konversi, yang merupakan penyalahgunaan konstruktor. Konstruktor tidak boleh melakukan logika, atau mereka berisiko gagal inisialisasi, meninggalkan Anda dengan objek yang tidak berguna.
Vince Emigh
JDK dipenuhi dengan desain yang mengerikan, saya bisa daftar sekitar 10 dari atas kepala saya. Desain perangkat lunak telah berkembang sejak aspek inti dari banyak bahasa diekspos secara publik, dan mereka bertahan karena kemungkinan melanggar kode jika ingin dirancang ulang untuk zaman modern.
Vince Emigh
1

init()metode bisa masuk akal ketika Anda memiliki objek yang membutuhkan sumber daya eksternal (seperti, misalnya, koneksi jaringan) yang secara bersamaan digunakan oleh objek lain. Anda mungkin tidak ingin / perlu menghabiskan sumber daya untuk seumur hidup objek. Dalam situasi seperti itu, Anda mungkin tidak ingin mengalokasikan sumber daya di konstruktor ketika alokasi sumber daya cenderung gagal.

Khususnya dalam pemrograman tertanam, Anda ingin memiliki jejak memori deterministik, jadi itu praktik yang umum (baik?) Untuk memanggil konstruktor Anda lebih awal, bahkan mungkin statis, dan hanya menginisialisasi nanti ketika kondisi tertentu terpenuhi.

Selain kasus-kasus seperti itu saya pikir semuanya harus masuk ke konstruktor.

tofro
sumber
Jika jenisnya membutuhkan koneksi, Anda harus menggunakan DI. Jika ada masalah saat membuat koneksi, Anda seharusnya tidak membuat objek yang membutuhkannya. Jika Anda mendorong pembuatan koneksi di dalam kelas, Anda akan membuat objek, objek itu akan instantiate dependensinya (koneksi). Jika instantiasi dependensi gagal, Anda berakhir dengan objek yang tidak dapat Anda gunakan, sehingga menghabiskan sumber daya.
Vince Emigh
Belum tentu. Anda berakhir dengan objek yang sementara tidak bisa Anda gunakan untuk semua tujuan. Dalam kasus seperti itu, objek hanya bisa bertindak sebagai antrian atau proksi sampai sumber daya tersedia. initMetode yang sepenuhnya mengutuk terlalu membatasi, IMHO.
tofro
0

Secara umum saya lebih suka konstruktor yang menerima semua argumen yang diperlukan untuk instance fungsional. Itu menjelaskan semua dependensi objek itu.

Di sisi lain, saya menggunakan kerangka kerja konfigurasi sederhana yang memerlukan konstruktor publik tanpa parameter, dan antarmuka untuk menyuntikkan dependensi dan nilai konfigurasi. Setelah itu selesai, kerangka konfigurasi memanggil initmetode objek: sekarang Anda menerima semua hal yang saya miliki untuk Anda, lakukan langkah-langkah terakhir untuk bersiap-siap untuk bekerja. Tetapi perhatikan: ini adalah kerangka konfigurasi yang secara otomatis memanggil metode init, sehingga Anda tidak akan lupa untuk menyebutnya.

Bernhard Hiller
sumber
0

Tidak ada bau kode jika metode init () - secara semantik tertanam dalam siklus keadaan-objek.

Jika Anda perlu memanggil init () untuk meletakkan objek ke dalam status yang konsisten, itu adalah bau kode.

Ada beberapa alasan teknis mengapa struktur seperti itu ada:

  1. kait kerangka kerja
  2. mengatur ulang objek ke kondisi awal (hindari redundansi)
  3. kemungkinan untuk menimpa selama tes
oopexpert
sumber
1
Tetapi mengapa seorang insinyur perangkat lunak menanamkannya dalam siklus hidup? Apakah ada tujuan yang dapat dibenarkan (mempertimbangkan hal itu mendorong objek yang dibangun sebagian) dan tidak dapat dihancurkan oleh alternatif yang lebih efisien? Saya merasa menanamkannya dalam siklus hidup akan menjadi bau kode, dan itu harus diganti dengan waktu pembuatan objek yang lebih baik (Mengapa mengalokasikan memori untuk objek jika Anda tidak berencana menggunakannya pada waktu itu? Mengapa membuat objek yang sebagian dibangun ketika Anda bisa menunggu sampai Anda benar-benar membutuhkan objek sebelum membuatnya?)
Vince Emigh
Intinya adalah bahwa objek tersebut harus dapat digunakan sebelum Anda memanggil metode init. Mungkin dengan cara lain daripada setelah dipanggil. Lihat pola keadaan. Dalam arti konstruksi objek parsial itu adalah bau kode.
oopexpert
-4

Nama init terkadang buram. Mari kita ambil mobil dan mesin. Untuk menyalakan mobil (hanya menghidupkan, mendengarkan radio), Anda ingin memastikan bahwa semua sistem siap digunakan.

Jadi Anda membangun mesin, pintu, roda dll. Layar Anda menunjukkan engine = off.

Tidak perlu mulai memantau mesin dll karena semuanya mahal. Kemudian ketika Anda memutar kunci untuk menyalakan Anda memanggil engine-> start. Itu mulai menjalankan semua proses mahal.

Sekarang Anda melihat engine = on. Dan proses pengapian dimulai.

Mobil tidak akan menyala tanpa mesin tersedia.

Anda dapat mengganti mesin dengan perhitungan kompleks Anda. Seperti sel Excel. Tidak semua sel harus aktif dengan semua penangan acara sepanjang waktu. Saat Anda fokus pada sel, Anda bisa memulainya. Meningkatkan kinerja seperti itu.

Luc Franken
sumber