Praktek yang diterima secara umum di antara kedua kasus ini:
function insertIntoDatabase(Account account, Otherthing thing) {
database.insertMethod(account.getId(), thing.getId(), thing.getSomeValue());
}
atau
function insertIntoDatabase(long accountId, long thingId, double someValue) {
database.insertMethod(accountId, thingId, someValue);
}
Dengan kata lain apakah umumnya lebih baik untuk melewatkan seluruh objek di sekitar atau hanya bidang yang Anda butuhkan?
Jawaban:
Tidak ada yang umumnya lebih baik dari yang lain. Ini panggilan penilaian yang harus Anda buat berdasarkan kasus per kasus.
Namun dalam praktiknya, ketika Anda berada dalam posisi yang benar-benar dapat membuat keputusan ini, itu karena Anda harus memutuskan lapisan mana dalam arsitektur program keseluruhan yang harus memecah objek menjadi primitif, jadi Anda harus memikirkan seluruh panggilan stack , bukan hanya metode yang Anda gunakan saat ini. Mungkin pemecahannya harus dilakukan di suatu tempat, dan itu tidak masuk akal (atau itu akan cenderung rawan kesalahan) untuk melakukannya lebih dari sekali. Pertanyaannya adalah di mana tempat itu seharusnya.
Cara termudah untuk membuat keputusan ini adalah dengan memikirkan kode apa yang harus atau tidak harus diubah jika objek diubah . Mari sedikit memperluas contoh Anda:
vs.
Dalam versi pertama, kode UI secara membabi buta melewati
data
objek dan terserah kode database untuk mengekstrak bidang yang berguna darinya. Pada versi kedua, kode UI memecahdata
objek menjadi bidang yang bermanfaat, dan kode database menerimanya secara langsung tanpa mengetahui dari mana asalnya. Implikasi utama adalah bahwa, jika strukturdata
objek berubah dalam beberapa cara, versi pertama hanya memerlukan kode database untuk berubah, sedangkan versi kedua hanya memerlukan kode UI untuk berubah . Yang mana dari keduanya yang benar sebagian besar tergantung pada data apa yangdata
dikandung oleh objek, tetapi biasanya sangat jelas. Misalnya, jikadata
adalah string yang disediakan pengguna seperti "20/05/1999", harusnya tergantung pada kode UI untuk mengonversinya menjadiDate
tipe yang tepat sebelum meneruskannya.sumber
Ini bukan daftar lengkap, tetapi pertimbangkan beberapa faktor berikut ketika memutuskan apakah suatu objek harus diteruskan ke metode sebagai argumen:
Apakah objek tidak berubah? Apakah fungsinya 'murni'?
Efek samping adalah pertimbangan penting untuk pemeliharaan kode Anda. Ketika Anda melihat kode dengan banyak objek stateable yang dapat diubah yang diedarkan di semua tempat, kode itu sering kurang intuitif (dengan cara yang sama bahwa variabel keadaan global sering menjadi kurang intuitif), dan debugging sering menjadi lebih sulit dan waktu- mengkonsumsi.
Sebagai pedoman praktis, bertujuan untuk memastikan, sejauh memungkinkan secara wajar, bahwa benda apa pun yang Anda lewati ke metode jelas tidak dapat diubah.
Hindari (sekali lagi, sejauh memungkinkan) desain apa pun di mana keadaan argumen diharapkan akan diubah sebagai akibat dari pemanggilan fungsi - salah satu argumen terkuat untuk pendekatan ini adalah Prinsip Least Astonancy ; yaitu seseorang yang membaca kode Anda dan melihat argumen yang dialihkan ke suatu fungsi 'kecil kemungkinannya' untuk mengharapkan keadaannya berubah setelah fungsi tersebut kembali.
Berapa banyak argumen yang sudah dimiliki metode ini?
Metode dengan daftar argumen yang terlalu panjang (bahkan jika sebagian besar argumen memiliki nilai 'default') mulai terlihat seperti bau kode. Namun, kadang-kadang fungsi seperti itu diperlukan, dan Anda mungkin mempertimbangkan untuk membuat kelas yang tujuan utamanya adalah bertindak seperti Obyek Parameter .
Pendekatan ini dapat melibatkan sejumlah kecil pemetaan kode boilerplate tambahan dari objek 'sumber' Anda ke objek parameter Anda, namun itu biaya yang cukup rendah baik dalam hal kinerja dan kompleksitas, dan ada sejumlah manfaat dalam hal decoupling dan objek kekekalan.
Apakah objek yang diteruskan milik secara eksklusif ke "lapisan" dalam aplikasi Anda (misalnya, Model ViewM, atau Entitas ORM?)
Pikirkan Separation of Concerns (SoC) . Kadang-kadang bertanya pada diri sendiri apakah objek "milik" pada lapisan atau modul yang sama di mana metode Anda ada (misalnya perpustakaan pembungkus tangan digulung, atau Lapisan Logika Bisnis inti Anda, dll.) Dapat menginformasikan apakah objek itu benar-benar harus diteruskan ke sana metode.
SoC adalah fondasi yang baik untuk menulis kode modular yang bersih dan longgar. misalnya, objek entitas ORM (pemetaan antara kode Anda dan skema Database Anda) idealnya tidak boleh dilewatkan di lapisan bisnis Anda, atau lebih buruk di lapisan presentasi / UI Anda.
Dalam hal melewatkan data di antara 'lapisan', memiliki parameter data biasa yang dilewatkan ke dalam metode biasanya lebih baik daripada lewat di objek dari lapisan 'salah'. Meskipun mungkin ide yang baik untuk memiliki model terpisah yang ada di lapisan 'kanan' yang bisa Anda petakan.
Apakah fungsinya sendiri terlalu besar dan / atau rumit?
Ketika suatu fungsi membutuhkan banyak item data, mungkin ada baiknya mempertimbangkan apakah fungsi itu mengambil terlalu banyak tanggung jawab; mencari peluang potensial untuk refactor menggunakan objek yang lebih kecil dan fungsi yang lebih pendek, lebih sederhana.
Haruskah fungsi tersebut menjadi objek perintah / permintaan?
Dalam beberapa kasus hubungan antara data dan fungsi mungkin dekat; dalam kasus-kasus tersebut pertimbangkan apakah Obyek Perintah atau Obyek Kueri akan sesuai.
Apakah menambahkan parameter objek ke metode memaksa kelas yang mengandung untuk mengadopsi dependensi baru?
Kadang-kadang argumen terkuat untuk argumen "Data lama polos" hanyalah bahwa kelas penerima sudah mandiri, dan menambahkan parameter objek ke salah satu metodenya akan mencemari kelas (atau jika kelas sudah tercemar, maka itu akan memperburuk entropi yang ada)
Apakah Anda benar-benar harus melewati objek yang lengkap atau hanya perlu sebagian kecil dari antarmuka objek itu?
Pertimbangkan Prinsip Segregasi Antarmuka sehubungan dengan fungsi Anda - yaitu ketika meneruskan objek, itu hanya akan bergantung pada bagian antarmuka argumen yang dibutuhkan (fungsi) itu sebenarnya.
sumber
Jadi ketika Anda membuat suatu fungsi, Anda secara implisit mendeklarasikan beberapa kontrak dengan kode yang memanggilnya. "Fungsi ini mengambil info ini, dan mengubahnya menjadi hal lain ini (mungkin dengan efek samping)".
Jadi, haruskah kontrak Anda secara logis dengan objek (namun mereka dilaksanakan), atau dengan bidang yang kebetulan menjadi bagian dari objek lain ini. Anda menambahkan penggandengan, tetapi sebagai programmer, Anda yang menentukan di mana tempatnya.
Secara umum , jika tidak jelas, maka nikmatilah data terkecil yang diperlukan agar fungsi berfungsi. Itu sering berarti hanya melewati bidang, karena fungsi tidak memerlukan hal-hal lain yang ditemukan di objek. Tetapi kadang-kadang mengambil seluruh objek lebih benar karena menghasilkan dampak yang lebih kecil ketika hal-hal yang tak terhindarkan berubah di masa depan.
sumber
Tergantung.
Untuk menguraikan, parameter yang diterima metode Anda harus secara semantik cocok dengan apa yang Anda coba lakukan. Pertimbangkan satu
EmailInviter
dan tiga kemungkinan implementasiinvite
metode ini:Melewati bagian
String
yang harus Anda lewatiEmailAddress
adalah cacat karena tidak semua string adalah alamat email. TheEmailAddress
kelas yang lebih baik semantik cocok perilaku metode ini. Namun lewat dalamUser
juga cacat karena mengapa harusEmailInviter
dibatasi untuk mengundang pengguna? Bagaimana dengan bisnis? Bagaimana jika Anda membaca alamat email dari file atau baris perintah dan itu tidak terkait dengan pengguna? Daftar surat? Daftarnya berlanjut.Ada beberapa tanda peringatan yang dapat Anda gunakan untuk panduan di sini. Jika Anda menggunakan tipe nilai sederhana seperti
String
atauint
tetapi tidak semua string atau int valid atau ada sesuatu yang "spesial" tentang mereka, Anda harus menggunakan tipe yang lebih bermakna. Jika Anda menggunakan objek dan satu-satunya yang Anda lakukan adalah memanggil pengambil, maka Anda harus meneruskan objek dalam pengambil secara langsung. Pedoman ini tidak sulit atau cepat, tetapi hanya sedikit pedoman.sumber
Kode Bersih merekomendasikan memiliki argumen sesedikit mungkin, yang berarti Obyek biasanya akan menjadi pendekatan yang lebih baik dan saya pikir itu masuk akal. karena
adalah panggilan yang lebih mudah dibaca daripada
sumber
Lewati objek, bukan negara penyusunnya. Ini mendukung prinsip berorientasi objek enkapsulasi dan penyembunyian data. Mengekspos jeroan objek dalam berbagai antarmuka metode yang tidak perlu melanggar prinsip OOP inti.
Apa yang terjadi jika Anda mengubah bidang
Otherthing
? Mungkin Anda mengubah jenis, menambah bidang, atau menghapus bidang. Sekarang semua metode seperti yang Anda sebutkan dalam pertanyaan Anda perlu diperbarui. Jika Anda melewati objek, tidak ada perubahan antarmuka.Satu-satunya waktu Anda harus menulis metode menerima bidang pada objek adalah ketika menulis metode untuk mengambil objek:
Pada titik melakukan panggilan itu, kode panggilan belum memiliki referensi ke objek karena titik memanggil metode itu adalah untuk mendapatkan objek.
sumber
Otherthing
?" (1) Itu akan melanggar prinsip terbuka / tertutup. (2) bahkan jika Anda memasukkan seluruh objek, kode di dalamnya lalu mengakses anggota objek itu (dan jika tidak, mengapa meneruskan objek?) Masih akan merusak ...Dari perspektif rawatan, argumen harus jelas dapat dibedakan satu sama lain, lebih disukai di tingkat kompiler.
Desain pertama mengarah ke deteksi dini bug. Desain kedua dapat menyebabkan masalah runtime halus yang tidak muncul dalam pengujian. Oleh karena itu desain pertama lebih disukai.
sumber
Dari keduanya, preferensi saya adalah metode pertama:
function insertIntoDatabase(Account account, Otherthing thing) { database.insertMethod(account.getId(), thing.getId(), thing.getSomeValue()); }
Alasannya adalah karena perubahan dilakukan pada objek mana pun di jalan, selama perubahan itu mempertahankan getter sehingga perubahan tersebut transparan di luar objek, maka Anda memiliki sedikit kode untuk diubah dan diuji serta lebih sedikit kemungkinan untuk mengganggu aplikasi.
Ini hanya proses pemikiran saya, sebagian besar didasarkan pada bagaimana saya suka bekerja dan menyusun hal-hal seperti ini dan yang terbukti sangat mudah dikelola dan dikelola dalam jangka panjang.
Saya tidak akan masuk ke konvensi penamaan tetapi akan menunjukkan bahwa meskipun metode ini memiliki kata "database" di dalamnya, mekanisme penyimpanan dapat berubah di jalan. Dari kode yang ditampilkan, tidak ada yang mengikat fungsi ke platform penyimpanan database yang digunakan - atau bahkan jika itu adalah database. Kami hanya berasumsi karena ada dalam nama. Sekali lagi, dengan anggapan para pengambil selalu diawetkan, mengubah cara / di mana benda-benda ini disimpan akan mudah.
Saya akan memikirkan kembali fungsi dan dua objek karena Anda memiliki fungsi yang memiliki ketergantungan pada dua struktur objek, dan khususnya getter yang dipekerjakan. Sepertinya fungsi ini mengikat kedua objek menjadi satu hal kumulatif yang bertahan. Perasaan saya memberi tahu saya bahwa objek ketiga mungkin masuk akal. Saya perlu tahu lebih banyak tentang objek-objek ini dan bagaimana mereka berhubungan dalam aktualitas dan peta jalan yang diantisipasi. Tapi nyali saya condong ke arah itu.
Saat kode berdiri sekarang, pertanyaannya adalah, "Di manakah fungsi ini atau seharusnya?" Apakah itu bagian dari Akun, atau OtherThing? Kemana perginya?
Saya kira sudah ada "database" objek ketiga, dan saya condong ke arah menempatkan fungsi ini ke objek itu, dan kemudian menjadi objek pekerjaan untuk dapat menangani Akun dan OtherThing, mengubah, dan kemudian juga bertahan hasilnya .
Jika Anda pergi sejauh membuat objek ke-3 sesuai dengan pola pemetaan objek-relasional (ORM), semua lebih baik. Itu akan membuatnya sangat jelas bagi siapa pun yang bekerja dengan kode untuk memahami "Ah, ini adalah tempat Akun dan OtherThing dihancurkan bersama dan bertahan".
Tetapi bisa juga masuk akal untuk memperkenalkan objek maju, yang menangani tugas menggabungkan dan mengubah Akun dan Lainnya, tetapi tidak menangani mekanisme bertahan. Saya akan melakukannya jika Anda mengantisipasi lebih banyak interaksi dengan atau di antara dua objek ini, karena pada saat itu tumbuh saya ingin bit kegigihan diperhitungkan menjadi objek yang mengelola kegigihan saja.
Saya akan memotret untuk menjaga desain sehingga salah satu dari Akun, OtherThing, atau objek ORM ketiga dapat diubah tanpa harus juga mengubah tiga lainnya. Kecuali ada alasan yang kuat untuk tidak melakukannya, saya ingin Akun dan Lainnya menjadi mandiri dan tidak harus mengetahui cara kerja dan struktur satu sama lain.
Tentu saja, jika saya tahu konteks lengkapnya, saya mungkin akan mengubah ide-ide saya sepenuhnya. Sekali lagi, ini hanya bagaimana saya berpikir ketika saya melihat hal-hal seperti ini, dan bagaimana lean.
sumber
Kedua pendekatan memiliki pro dan kontra mereka sendiri. Apa yang lebih baik dalam skenario tergantung banyak pada kasus penggunaan yang ada.
Pro Multi params, referensi Obyek Con:
Referensi Obyek Pro:
Jadi, apa yang perlu digunakan dan kapan banyak bergantung pada use-case
sumber
Di satu sisi Anda memiliki Akun dan objek Otherthing. Di sisi lain, Anda memiliki kemampuan untuk memasukkan nilai ke dalam basis data, mengingat id akun dan id Otherthing. Itulah dua hal yang diberikan.
Anda dapat menulis metode menggunakan Akun dan Lainnya sebagai argumen. Di sisi pro, penelepon tidak perlu mengetahui detail tentang Akun dan Lainnya. Di sisi negatif, callee perlu tahu tentang metode Akun dan Lainnya. Dan juga, tidak ada cara untuk memasukkan hal lain ke dalam database selain nilai objek Otherthing dan tidak ada cara untuk menggunakan metode ini jika Anda memiliki id dari objek akun, tetapi bukan objek itu sendiri.
Atau Anda dapat menulis metode yang menggunakan dua id dan nilai sebagai argumen. Di sisi negatif, penelepon perlu mengetahui rincian Akun dan Lainnya. Dan mungkin ada situasi di mana Anda benar-benar membutuhkan lebih banyak detail dari Akun atau Lainnya dari sekadar id untuk dimasukkan ke dalam database, dalam hal ini solusi ini sama sekali tidak berguna. Di sisi lain, semoga tidak ada pengetahuan tentang Akun dan Hal Lain yang diperlukan di masa kecil, dan ada lebih banyak fleksibilitas.
Panggilan penilaian Anda: Apakah lebih banyak fleksibilitas diperlukan? Ini sering kali bukan masalah satu panggilan, tetapi akan konsisten melalui semua perangkat lunak Anda: Entah Anda menggunakan id sebagian besar waktu, atau Anda menggunakan objek. Mencampurnya membuat Anda menjadi yang terburuk dari kedua dunia.
Di C ++, Anda dapat memiliki metode yang mengambil dua id plus nilai, dan metode inline mengambil Akun dan Lainnya, sehingga Anda memiliki kedua cara dengan nol overhead.
sumber