Izinkan saya mengawali ini dengan mengatakan ini bukan kode saya atau kode rekan kerja saya. Bertahun-tahun yang lalu ketika perusahaan kami lebih kecil, kami memiliki beberapa proyek yang perlu kami lakukan sehingga kami tidak memiliki kapasitas untuk itu, jadi mereka di-outsource. Sekarang, saya tidak menentang outsourcing atau kontraktor pada umumnya, tetapi basis kode yang mereka hasilkan adalah sejumlah besar WTF. Yang sedang berkata, itu (kebanyakan) bekerja, jadi saya kira itu dalam 10% teratas dari proyek outsourcing yang pernah saya lihat.
Seiring berkembangnya perusahaan kami, kami telah mencoba mengambil alih lebih banyak dari pengembangan kami secara langsung. Proyek khusus ini mendarat di pangkuan saya jadi saya sudah membahasnya, membersihkannya, menambahkan tes, dll.
Ada satu pola yang saya lihat berulang-ulang dan tampaknya sangat mengerikan sehingga saya bertanya-tanya apakah mungkin ada alasan dan saya tidak melihatnya. Pola adalah objek tanpa metode publik atau anggota, hanya konstruktor publik yang melakukan semua pekerjaan objek.
Misalnya, (kodenya di Jawa, jika itu penting, tapi saya harap ini menjadi pertanyaan yang lebih umum):
public class Foo {
private int bar;
private String baz;
public Foo(File f) {
execute(f);
}
private void execute(File f) {
// FTP the file to some hardcoded location,
// or parse the file and commit to the database, or whatever
}
}
Jika Anda bertanya-tanya, jenis kode ini sering disebut dengan cara berikut:
for(File f : someListOfFiles) {
new Foo(f);
}
Sekarang, saya telah diajarkan sejak lama bahwa objek instantiated dalam satu lingkaran pada umumnya adalah ide yang buruk, dan bahwa konstruktor harus melakukan pekerjaan minimum. Melihat kode ini sepertinya akan lebih baik untuk menjatuhkan konstruktor dan membuat execute
metode statis publik.
Saya memang bertanya kepada kontraktor mengapa itu dilakukan dengan cara ini, dan respons yang saya dapatkan adalah "Kami dapat mengubahnya jika Anda mau". Yang tidak terlalu membantu.
Ngomong-ngomong, apakah ada alasan untuk melakukan hal seperti ini, dalam bahasa pemrograman apa pun, atau apakah ini sekadar pengajuan ke Daily WTF?
public static void main(string[] args)
dan yang pernah mendengar objek, kemudian mencoba untuk menumbuknya bersama.Jawaban:
Ok, turun daftar:
Saya sudah lama diajarkan bahwa objek instantiated dalam satu lingkaran umumnya adalah ide yang buruk
Tidak dalam bahasa apa pun yang saya gunakan.
Dalam C itu adalah ide yang baik untuk mendeklarasikan variabel Anda di muka, tetapi itu berbeda dari apa yang Anda katakan. Mungkin sedikit lebih cepat jika Anda mendeklarasikan objek di atas loop dan menggunakannya kembali, tetapi ada banyak bahasa di mana peningkatan kecepatan ini akan menjadi tidak berarti (dan mungkin beberapa kompiler di luar sana yang melakukan optimasi untuk Anda :)).
Secara umum, jika Anda membutuhkan objek dalam satu lingkaran, buat satu.
konstruktor harus melakukan pekerjaan minimum
Konstruktor harus instantiate bidang objek dan melakukan inisialisasi lain yang diperlukan untuk membuat objek siap digunakan. Ini umumnya berarti konstruktor kecil, tetapi ada skenario di mana ini akan menjadi sejumlah besar pekerjaan.
apakah pernah ada alasan untuk melakukan hal seperti ini, dalam bahasa pemrograman apa pun, atau apakah ini hanya penyerahan lain kepada Daily WTF?
Ini adalah pengiriman ke Daily WTF. Memang, ada hal-hal buruk yang dapat Anda lakukan dengan kode. Masalahnya adalah bahwa penulis memiliki kesalahpahaman yang luas tentang kelas apa dan bagaimana menggunakannya. Secara khusus, inilah yang saya lihat salah dengan kode ini:
sumber
Bagi saya, ini sedikit mengejutkan ketika Anda mendapatkan objek melakukan banyak pekerjaan di konstruktor, jadi hanya pada itu saya akan merekomendasikan menentangnya (prinsip kejutan paling tidak).
Upaya contoh yang baik :
Saya tidak yakin apakah penggunaan ini anti-pola, tetapi dalam C # Saya telah melihat kode yang sesuai dengan (contoh yang agak dibuat-buat):
Dalam hal ini,
ImpersonationContext
kelas melakukan semua pekerjaannya di konstruktor dan metode Buang. Itu mengambil keuntungan dari pernyataan C #using
, yang cukup dipahami dengan baik oleh pengembang C #, dan dengan demikian menjamin bahwa pengaturan yang terjadi dalam konstruktor dibatalkan setelah kita berada di luarusing
pernyataan, bahkan jika pengecualian terjadi. Ini mungkin penggunaan terbaik yang pernah saya lihat untuk ini (yaitu "bekerja" di konstruktor), meskipun saya masih tidak yakin saya akan menganggapnya "baik". Dalam hal ini, itu cukup jelas, fungsional dan cepat.Contoh pola yang baik dan serupa akan menjadi pola perintah . Anda pasti tidak mengeksekusi logika di konstruktor di sana, tetapi itu serupa dalam arti bahwa seluruh kelas setara dengan doa metode (termasuk parameter yang diperlukan untuk memanggil metode). Dengan cara ini, informasi pemanggilan metode dapat diserialisasi atau diteruskan untuk penggunaan selanjutnya, yang sangat berguna. Mungkin kontraktor Anda mencoba sesuatu yang serupa, walaupun saya sangat meragukannya.
Komentar pada contoh Anda:
Saya akan mengatakan itu adalah anti-pola yang sangat pasti. Ia tidak menambahkan apa-apa, bertentangan dengan apa yang diharapkan, dan melakukan pemrosesan yang terlalu lama di konstruktor (FTP dalam konstruktor? WTF memang, meskipun saya kira orang telah dikenal untuk memanggil layanan web dengan cara ini).
Saya berjuang untuk menemukan kutipan, tetapi buku Grady Booch "Object Solutions" memiliki anekdot tentang tim pengembang C yang beralih ke C ++. Rupanya mereka telah melalui pelatihan, dan diberitahu oleh manajemen bahwa mereka benar-benar harus melakukan hal-hal yang "berorientasi objek". Dibawa ke dalam untuk mencari tahu apa yang salah, penulis menggunakan alat metrik kode, dan menemukan bahwa jumlah rata-rata metode per kelas tepat 1, dan variasi kalimat "Do It". Saya harus mengatakan, saya belum pernah mengalami masalah khusus ini sebelumnya, tetapi ternyata itu bisa menjadi gejala orang dipaksa untuk membuat kode berorientasi objek, tanpa benar-benar mengerti bagaimana melakukannya, dan mengapa.
sumber
Tidak.
Jika semua pekerjaan dilakukan di konstruktor itu berarti objek itu tidak pernah dibutuhkan sejak awal. Kemungkinan besar, kontraktor hanya menggunakan objek untuk modularitas. Dalam hal itu, kelas non-instantiated dengan metode statis akan mendapatkan manfaat yang sama tanpa membuat instance objek yang berlebihan.
Hanya ada satu kasus di mana melakukan semua pekerjaan dalam konstruktor objek dapat diterima. Saat itulah tujuan objek secara khusus untuk mewakili identitas unik jangka panjang, daripada melakukan pekerjaan. Dalam kasus seperti itu, objek akan disimpan untuk alasan selain melakukan pekerjaan yang bermakna. Misalnya, contoh tunggal.
sumber
Saya tentu saja membuat banyak kelas yang melakukan banyak pekerjaan untuk menghitung sesuatu di konstruktor, tetapi objeknya tidak berubah, sehingga membuat hasil perhitungan itu tersedia melalui properti, dan kemudian tidak pernah melakukan hal lain. Itu kekekalan normal dalam aksi.
Saya pikir yang aneh di sini adalah memasukkan kode dengan efek samping ke dalam konstruktor. Membangun objek dan membuangnya tanpa mengakses properti atau metode terlihat aneh (tapi setidaknya dalam hal ini cukup jelas bahwa ada sesuatu yang sedang terjadi).
Ini akan lebih baik jika fungsi dipindahkan ke metode publik, dan kemudian memiliki instance dari kelas yang disuntikkan, kemudian minta for loop memanggil metode tersebut berulang kali.
sumber
Tidak.
Seseorang dapat dengan wajar mempertanyakan pedoman ini dan berkata, "Tapi saya tidak mengikuti aturan ini, dan kode saya berfungsi dengan baik!" Untuk itu, saya akan menjawab, "Yah, itu mungkin benar, sampai tidak."
sumber
Tampaknya bagi saya bahwa orang yang melakukan ini sedang mencoba untuk meretas batasan Jawa yang tidak memungkinkan untuk melewati fungsi sebagai warga negara kelas satu. Setiap kali Anda harus melewati suatu fungsi, Anda harus membungkusnya di dalam kelas dengan metode suka
apply
atau mirip.Jadi saya kira dia hanya menggunakan jalan pintas dan langsung menggunakan kelas sebagai pengganti fungsi. Setiap kali Anda ingin menjalankan fungsi, Anda hanya instantiate kelas.
Ini jelas bukan pola yang baik, setidaknya karena kami di sini mencoba mencari tahu, tapi saya kira itu mungkin cara paling tidak tepat untuk melakukan sesuatu yang mirip dengan pemrograman fungsional di Jawa.
sumber
Bagi saya aturan praktis adalah tidak melakukan apa pun di konstruktor, kecuali untuk menginisialisasi variabel anggota. Alasan pertama untuk itu adalah membantu untuk mengikuti prinsip SRP, karena biasanya proses inisialisasi yang panjang merupakan indikasi bahwa kelas melakukan lebih dari yang seharusnya, dan inisialisasi harus dilakukan di suatu tempat di luar kelas. Alasan kedua adalah bahwa dengan cara ini hanya parameter yang diperlukan yang dilewati, sehingga Anda membuat kode yang lebih sedikit digabungkan. Konstruktor dengan inisialisasi yang rumit biasanya membutuhkan parameter yang hanya digunakan untuk membangun objek lain, tetapi yang tidak digunakan oleh kelas asli.
sumber
Saya akan menentang arus di sini - dalam bahasa di mana semuanya harus berada dalam beberapa kelas DAN Anda tidak dapat memiliki kelas statis, Anda dapat mengalami situasi di mana Anda memiliki sedikit fungsionalitas terisolasi yang perlu taruh di suatu tempat dan tidak cocok dengan apa pun. Pilihan Anda adalah kelas utilitas yang mencakup berbagai bit fungsionalitas yang tidak terkait, atau kelas yang pada dasarnya hanya melakukan hal yang satu ini.
Jika Anda hanya memiliki satu bit seperti ini, maka kelas statis Anda adalah kelas spesifik Anda. Jadi, Anda memiliki konstruktor yang melakukan semua pekerjaan, atau fungsi tunggal yang melakukan semua pekerjaan. Keuntungan melakukan segala sesuatu di konstruktor adalah bahwa hal itu terjadi hanya sekali, memungkinkan Anda untuk menggunakan bidang pribadi, properti, dan metode tanpa khawatir mereka akan digunakan kembali atau threading. Jika Anda memiliki konstruktor / metode, Anda mungkin tergoda untuk membiarkan parameter diteruskan ke metode tunggal, tetapi jika Anda menggunakan bidang pribadi atau properti yang memperkenalkan potensi masalah threading.
Bahkan dengan kelas utilitas, kebanyakan orang tidak akan berpikir untuk memiliki kelas statis bersarang (dan itu mungkin tidak mungkin tergantung pada bahasanya).
Pada dasarnya ini adalah cara yang relatif dapat dipahami untuk mengisolasi perilaku dari sisa sistem, dalam apa yang diizinkan oleh bahasa, sambil tetap memungkinkan Anda untuk mengambil keuntungan dari sebanyak mungkin bahasa.
Terus terang saya akan menjawab sebanyak yang kontraktor Anda lakukan, itu adalah penyesuaian kecil untuk mengubahnya menjadi dua panggilan, tidak ada banyak yang merekomendasikan satu di atas yang lain. Apa dis / advatanges ada, mungkin lebih hipotetis daripada yang sebenarnya, mengapa mencoba untuk membenarkan keputusan ketika itu tidak masalah dan mungkin tidak begitu banyak diputuskan hanya sebagai tindakan default?
sumber
Ada pola-pola umum dalam bahasa di mana fungsi dan kelas lebih banyak hal yang sama, seperti Python, di mana orang menggunakan objek pada dasarnya menjadi fungsi dengan efek samping terlalu banyak atau dengan beberapa objek bernama dikembalikan.
Dalam hal ini, sebagian besar, jika tidak semua pekerjaan dapat dilakukan dalam konstruktor, karena objek itu sendiri hanyalah pembungkus di sekitar satu bundel pekerjaan, dan tidak ada alasan yang baik untuk memasukkannya ke dalam fungsi yang terpisah. Sesuatu seperti
benar-benar tidak lebih baik daripada
Alih-alih langsung memiliki efek samping, Anda dapat memanggil objek untuk menegakkan efek samping pada isyarat, yang memberi Anda visibilitas yang lebih baik. Jika saya akan memiliki efek samping yang buruk pada suatu objek, saya dapat melakukan semua pekerjaan saya dan kemudian melewati objek saya akan berdampak buruk dan melakukan kerusakan saya untuk itu. Mengidentifikasi objek dan mengisolasi panggilan dengan cara ini lebih jelas daripada melewatkan beberapa objek tersebut ke suatu fungsi sekaligus.
Anda dapat membuat beberapa nilai pengembalian melalui pengakses pada objek. Semua pekerjaan sudah selesai, tetapi saya ingin semuanya dalam konteks, dan saya tidak benar-benar ingin meneruskan banyak referensi ke fungsi saya.
Jadi sebagai programmer Python / C # bersama, ini sepertinya tidak aneh bagi saya.
sumber
Satu kasus muncul di pikiran di mana konstruktor / penghancur melakukan semua pekerjaan:
Kunci. Anda biasanya tidak pernah berinteraksi dengan kunci selama masa pakainya. Arsitektur penggunaan C # baik untuk menangani kasus seperti itu tanpa secara eksplisit merujuk pada destruktor. Tidak semua kunci diimplementasikan dengan cara ini.
Saya juga telah menulis apa yang berjumlah sedikit kode konstruktor untuk menambal bug perpustakaan runtime. (Sebenarnya memperbaiki kode akan menyebabkan masalah lain.) Tidak ada destruktor karena tidak perlu membatalkan tambalan.
sumber
Saya setuju dengan AndyBursh. Sepertinya ada sedikit masalah pengetahuan.
Namun penting untuk menyebutkan kerangka kerja seperti NHibernate yang mengharuskan Anda hanya untuk kode dalam konstruktor (saat membuat kelas peta). Namun ini bukan batasan kerangka kerja, itu hanya apa yang Anda butuhkan. Ketika datang ke refleksi, konstruktor itu satu-satunya metode yang akan selalu ada (walaupun mungkin pribadi) dan ini mungkin membantu jika Anda harus memanggil metode kelas secara dinamis.
sumber
Ya, mungkin Anda mengingat mengapa kami membutuhkan StringBuilder.
Ada dua sekolah di Jawa.
Pembangun dipromosikan oleh yang pertama , yang mengajarkan kita untuk menggunakan kembali (karena berbagi = identitas ke efisiensi). Namun, pada awal tahun 2000 saya mulai membaca bahwa membuat objek di Jawa sangat murah (dibandingkan dengan C ++) dan GC sangat efisien sehingga penggunaan kembali tidak berharga dan satu-satunya hal yang penting adalah produktivitas programmer. Untuk produktivitas, ia harus menulis kode yang dapat dikelola, yang berarti modularisasi (alias mempersempit ruang lingkup). Begitu,
ini buruk
ini bagus.
Saya mengajukan pertanyaan ini ketika forum.java.sun ada, dan orang-orang setuju bahwa mereka akan lebih suka mendeklarasikan array sesempit mungkin.
Programmer java modern tidak pernah ragu untuk mendeklarasikan kelas baru atau membuat objek. Dia didorong untuk melakukannya. Java memberikan semua alasan untuk melakukannya, dengan gagasan bahwa "semuanya adalah objek" dan penciptaan yang murah.
Lebih jauh, gaya pemrograman perusahaan modern adalah, ketika Anda perlu menjalankan suatu tindakan, Anda membuat kelas khusus (atau bahkan lebih baik, sebuah kerangka kerja) yang akan menjalankan tindakan sederhana itu. Good Java IDEs, itu membantu di sini. Mereka menghasilkan banyak omong kosong secara otomatis, seperti bernafas.
Jadi, saya pikir objek Anda tidak memiliki pola Builder atau Factory. Tidak layak membuat instance secara langsung, oleh konstruktor. Kelas pabrik khusus untuk setiap objek Anda disarankan.
Sekarang, ketika pemrograman fungsional mulai dimainkan, membuat objek menjadi lebih sederhana. Anda akan membuat objek di setiap langkah sebagai bernafas, tanpa menyadarinya. Jadi, saya berharap kode Anda akan menjadi lebih "efisien" di era baru
Secara pribadi, saya tidak melihat alasan dalam pedoman ini. Saya menganggapnya sebagai metode lain. Kode anjak keluar diperlukan hanya ketika Anda dapat membagikannya.
sumber