Menghindari konstruktor dengan banyak argumen

10

Jadi saya punya pabrik yang membuat objek dari berbagai kelas. Semua kelas yang mungkin berasal dari nenek moyang abstrak. Pabrik memiliki file konfigurasi (sintaks JSON) dan memutuskan kelas mana yang akan dibuat, tergantung pada konfigurasi pengguna.

Untuk mencapai ini, pabrik menggunakan boost :: property_tree untuk penguraian JSON. Dia berjalan melalui ptree dan memutuskan objek konkret yang akan dibuat.

Namun, objek produk memiliki banyak bidang (atribut). Bergantung pada kelas konkret, objek memiliki sekitar 5-10 atribut, di masa depan mungkin bahkan lebih.

Jadi saya tidak yakin bagaimana seharusnya konstruktor dari objek tersebut. Saya dapat memikirkan dua solusi:

1) Konstruktor produk mengharapkan setiap atribut sebagai parameter, dengan demikian, konstruktor tersebut akan berakhir dengan 10+ parameter. Ini akan menjadi jelek dan mengarah pada baris kode yang panjang dan tidak dapat dibaca. Namun, keuntungannya adalah bahwa pabrik dapat mengurai JSON dan memanggil konstruktor dengan parameter yang benar. Kelas produk tidak perlu tahu bahwa itu telah dibuat karena konfigurasi JSON. Tidak perlu tahu ada JSON atau konfigurasi yang terlibat sama sekali.

2) Konstruktor produk hanya mengharapkan satu argumen, objek property_tree. Maka dapat mem-parsing informasi yang dibutuhkan. Jika ada informasi dalam konfigurasi yang hilang atau di luar batas, setiap kelas produk dapat bereaksi dengan benar. Pabrik tidak perlu tahu argumen apa yang dibutuhkan oleh beberapa produk. Pabrik juga tidak perlu tahu bagaimana harus bereaksi jika konfigurasi salah. Dan antarmuka konstruktor bersatu dan kecil. Tetapi, sebagai kerugian, produk perlu mengekstraksi informasi yang diperlukan dari JSON, sehingga, ia tahu bagaimana itu dibangun.

Saya cenderung lebih suka solusi 2). Namun, saya tidak yakin apakah ini pola pabrik yang baik. Entah bagaimana rasanya membiarkan produk tahu bahwa itu dibuat dengan konfigurasi JSON. Di sisi lain, produk baru dapat diperkenalkan dengan sangat sederhana.

Ada pendapat tentang itu?

lugge86
sumber
1
Saya mengikuti tautan Anda. Ada contoh dalam jawaban berperingkat teratas dari ratchet freak. Tetapi masalah apa yang dipecahkan oleh "pembangun" ini? Ada kode-line DataClass data = builder.createResult () ;. Tetapi metode createResults () - masih harus mendapatkan 10 parameter ke dalam objek DataClass. Tapi bagaimana caranya? Tampaknya Anda hanya memiliki satu lapisan abstraksi lagi, tetapi konstruktor DataClass tidak menjadi lebih kecil.
lugge86
Lihatlah pembangun dan prototipe.
Silviu Burcea
Silviu Burcea, saya lakukan. Namun, ketika menggunakan pembangun, bagaimana pembangun memasukkan parameter ke dalam produk? Di suatu tempat di sana TELAH MENJADI antarmuka yang gemuk. Builder hanya satu lapisan lagi, tetapi entah bagaimana parameternya harus menemukan jalan mereka ke kelas produk.
lugge86
1
Jika kelas Anda terlalu besar, mengubah argumen konstruktor tidak akan menjadikannya tidak terlalu besar .
Telastyn

Jawaban:

10

Saya tidak akan melakukan opsi 2, karena dengan begitu Anda telah selamanya melilit konstruksi objek Anda dengan meningkatkan parsing pohon properti. Jika Anda nyaman dengan kelas yang membutuhkan banyak parameter, Anda harus merasa nyaman dengan konstruktor yang membutuhkan banyak parameter, seperti hidup!

Jika masalah utama Anda adalah keterbacaan kode, Anda dapat menggunakan pola builder, itu pada dasarnya adalah c ++ / java sementara karena kurangnya argumen bernama. Anda berakhir dengan hal-hal yang terlihat seperti ini:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Jadi sekarang MyObject akan memiliki konstruktor pribadi, yang dipanggil di Builder :: build. Yang menyenangkan adalah bahwa itu akan menjadi satu-satunya tempat Anda pernah memanggil konstruktor dengan 10 parameter. Pabrik tree properti boost akan menggunakan builder, dan selanjutnya jika Anda ingin membangun MyObject secara langsung atau dari sumber yang berbeda, Anda akan melalui builder. Dan pembangun pada dasarnya memungkinkan Anda memberi nama setiap parameter dengan jelas saat Anda meneruskannya, sehingga lebih mudah dibaca. Ini jelas menambahkan beberapa boilerplate, jadi Anda harus memutuskan apakah itu layak dibandingkan dengan hanya memanggil konstruktor yang berantakan, atau menyatukan beberapa parameter Anda yang ada ke dalam struct, dll. Hanya melemparkan opsi lain di atas meja.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example

Nir Friedman
sumber
5

JANGAN gunakan pendekatan kedua.

Ini jelas bukan solusi dan hanya akan mengarah pada instantiating kelas dalam logika bisnis Anda, bukan bagian dari aplikasi Anda di mana pabrik berada.

Antara:

  • cobalah untuk mengelompokkan parameter tertentu yang tampaknya mewakili hal-hal serupa ke dalam objek
  • membagi kelas saat ini menjadi beberapa kelas yang lebih kecil (memiliki kelas layanan dengan 10 parameter sepertinya kelas melakukan terlalu banyak hal)
  • biarkan apa adanya, jika kelas Anda sebenarnya bukan layanan, tetapi sebuah objek nilai

Kecuali objek yang Anda buat sebenarnya adalah kelas yang bertanggung jawab untuk menyimpan data, Anda harus mencoba untuk memperbaiki kode dan membagi kelas besar menjadi yang lebih kecil.

Andy
sumber
Nah, logika bsiness menggunakan pabrik yang mengembalikan produk, dengan demikian, logika bisnis tidak melihat JSON / ptree stuff. Tapi saya mengerti maksud Anda, memiliki kode Parser di konstrucotr terasa salah.
lugge86
Kelas mewakili widget dalam GUI untuk sistem tertanam, dengan demikian, 5+ atribut tampaknya OK untuk saya: x_coord, y_coord, backgroundcolor, framesize, framecolor, text ...
lugge86
1
@ lugge86 Meskipun Anda menggunakan pabrik untuk mem-parse JSON, dan dengan demikian menghindari memanggil newatau membuat objek di dalam logika bisnis Anda, itu bukan desain yang sangat bagus. Periksa Pembicaraan jangan mencari hal-hal oleh Miško Hevery , yang menjelaskan secara lebih mendalam mengapa pendekatan pabrik yang Anda isyaratkan buruk dari sudut pandang pengujian dan membaca. Juga, kelas Anda tampaknya menjadi objek data, dan bagi mereka umumnya boleh saja memiliki lebih banyak parameter daripada kelas layanan reguler. Saya tidak akan terlalu terganggu.
Andy
Saya merasa baik-baik saja dengan pendekatan pabrik saya, tetapi saya akan mengikuti tautan Anda dan memikirkannya. Namun, pabrik tidak dipertanyakan dalam topik ini. Pertanyaannya adalah bagaimana mengatur produk ...
lugge86
"Memiliki kelas layanan dengan 10 parameter sepertinya kelas melakukan terlalu banyak hal" Tidak dalam pembelajaran mesin. Algoritma ML apa pun akan memiliki banyak parameter yang bisa ditala. Saya bertanya-tanya apa cara yang tepat untuk menghadapinya ketika mengkode ML.
Siyuan Ren
0

Opsi 2 hampir benar.

Opsi yang ditingkatkan 2

Buat kelas "menghadap ke depan" yang tugasnya adalah untuk mengambil objek struktur JSON dan memilih bit dan memanggil konstruktor pabrik. Dibutuhkan apa yang pabrik buat dan berikan ke klien.

  • Pabrik sama sekali tidak tahu bahwa JSON thingy bahkan ada.
  • Klien tidak harus tahu bit spesifik apa yang dibutuhkan pabrik.

Pada dasarnya "ujung depan" mengatakan kepada 2 Bob: "Saya berurusan dengan pelanggan yang telah dihapus sehingga para insinyur tidak perlu melakukannya! Saya memiliki keterampilan orang-orang!" Tom yang malang. Jika dia hanya mengatakan, "Saya memisahkan klien dari konstruksi. Hasil ini adalah pabrik yang sangat kohesif"; dia mungkin mempertahankan pekerjaannya.

Terlalu Banyak Argumen?

Bukan untuk klien - komunikasi ujung depan.

Front end - pabrik? Jika tidak 10 parameter maka yang terbaik yang dapat Anda lakukan adalah menunda membongkar, jika bukan JSON asli maka beberapa DTO. Apakah ini lebih baik daripada melewatkan JSON ke pabrik? Perbedaan yang sama saya katakan.

Saya akan sangat mempertimbangkan melewati parameter individual. Tetap berpegang pada tujuan pabrik yang bersih dan kohesif. Hindari kekhawatiran jawaban @DavidPacker.

Mengurangi "terlalu banyak argumen"

  • Konstruktor pabrik atau kelas

    • hanya mengambil argumen untuk konstruksi kelas / objek tertentu.
    • parameter standar
    • parameter opsional
    • argumen bernama
  • Pengelompokan argumen front end

    • Memeriksa, mengevaluasi, memvalidasi, set, dll. Nilai argumen dipandu oleh tanda tangan konstruktor di atas.
radarbob
sumber
"Pabrik sama sekali tidak tahu bahwa JSON thingy bahkan ada" - baik, lalu untuk apa pabrik ?? Ini menyembunyikan detail penciptaan produk baik dari produk maupun konsumen. Mengapa kelas lain harus membantu? Saya baik-baik saja dengan JSON yang berbicara di pabrik. Satu dapat menerapkan pabrik lain untuk parsing XML dan menerapkan "Pabrik Abstrak" di masa depan ...
lugge86
Tn. Factory: "Obyek apa yang Anda inginkan? ... Apa itu? Cukup beri tahu saya objek kelas apa yang akan dibangun." File konfigurasi JSON adalah sumber data, seperti yang dikatakan Paman Bob "ini detail implementasi." Bisa dari sumber lain dan / atau dalam bentuk lain. Secara umum kami ingin memisahkan dari detail sumber data tertentu. Jika sumber atau bentuk berubah, pabrik tidak akan. Diberi sumber + parser, dan pabrik sebagai modul yang dipisahkan membuat keduanya dapat digunakan kembali.
radarbob