Kami memiliki lapisan data yang membungkus Linq To SQL. Dalam datalayer ini kita memiliki metode ini (disederhanakan)
int InsertReport(Report report)
{
db.Reports.InsertOnSubmit(report);
db.SubmitChanges();
return report.ID;
}
Saat mengirim perubahan, ID laporan diperbarui dengan nilai dalam basis data yang kemudian kami kembalikan.
Dari sisi panggilan sepertinya ini (disederhanakan)
var report = new Report();
DataLayer.InsertReport(report);
// Do something with report.ID
Melihat kode, ID telah diatur di dalam fungsi InsertReport sebagai semacam efek samping, dan kemudian kita mengabaikan nilai kembali.
Pertanyaan saya adalah, haruskah saya mengandalkan efek samping dan melakukan sesuatu seperti ini sebagai gantinya.
void InsertReport(Report report)
{
db.Reports.InsertOnSubmit(report);
db.SubmitChanges();
}
atau haruskah kita mencegahnya
int InsertReport(Report report)
{
var newReport = report.Clone();
db.Reports.InsertOnSubmit(newReport);
db.SubmitChanges();
return newReport.ID;
}
bahkan mungkin
Report InsertReport(Report report)
{
var newReport = report.Clone();
db.Reports.InsertOnSubmit(newReport);
db.SubmitChanges();
return newReport;
}
Pertanyaan ini muncul ketika kami membuat unit test dan menemukan bahwa tidak benar-benar jelas bahwa properti ID parameter laporan akan diperbarui dan bahwa untuk mengejek perilaku efek samping terasa salah, kode akan berbau jika Anda mau.
sumber
Jawaban:
Ya, tidak apa-apa, dan cukup umum. Ini bisa menjadi tidak jelas, seperti yang Anda temukan.
Secara umum, saya cenderung memiliki metode tipe kegigihan untuk mengembalikan instance objek yang diperbarui. Itu adalah:
Ya, Anda mengembalikan objek yang sama dengan yang Anda lewati, tetapi itu membuat API lebih jelas. Tidak perlu untuk mengkloning - jika ada sesuatu yang akan menyebabkan kebingungan jika, seperti dalam kode asli Anda, penelepon terus menggunakan objek yang mereka lewati.
Pilihan lain adalah menggunakan DTO
Dengan begitu API sangat jelas, dan penelepon tidak dapat secara tidak sengaja mencoba dan menggunakan objek yang diteruskan / dimodifikasi. Bergantung pada apa yang dilakukan kode Anda, itu bisa sedikit menyusahkan.
sumber
IMO ini adalah contoh langka di mana efek samping dari perubahan diinginkan - karena entitas Laporan Anda MEMILIKI ID, itu sudah bisa dianggap memiliki kekhawatiran DTO, dan bisa dibilang ada kewajiban ORM untuk memastikan bahwa dalam memori Laporan entitas disimpan selaras dengan objek representasi basis data.
sumber
Masalahnya adalah bahwa tanpa dokumentasi, tidak jelas sama sekali apa yang dilakukan metode ini dan terutama mengapa mengembalikan integer.
Solusi termudah adalah menggunakan nama yang berbeda untuk metode Anda. Sesuatu seperti:
Namun, ini tidak cukup ambigu: jika, seperti dalam C #, instance dari
report
objek dilewatkan dengan referensi, akan sulit untuk mengetahui apakahreport
objek asli dimodifikasi, atau jika metode mengkloningnya dan memodifikasi hanya klon. Jika Anda memilih untuk memodifikasi objek asli, akan lebih baik untuk memberi nama metode:Solusi yang lebih rumit (dan mungkin kurang optimal) adalah sangat memperbaiki kode. Bagaimana dengan:
sumber
Biasanya programmer berharap bahwa hanya metode instance objek yang dapat mengubah kondisinya. Dengan kata lain, saya tidak terkejut jika
report.insert()
mengubah id laporan, dan mudah untuk memeriksa. Tidaklah mudah untuk bertanya-tanya untuk setiap metode di seluruh aplikasi apakah itu mengubah id laporan atau tidak.Saya juga berpendapat bahwa mungkin
ID
seharusnya tidak menjadi milikReport
sama sekali. Karena tidak berisi id yang valid untuk waktu yang lama, Anda benar-benar memiliki dua objek berbeda sebelum dan setelah penyisipan, dengan perilaku yang berbeda. Objek "sebelum" dapat dimasukkan, tetapi tidak dapat diambil, diperbarui, atau dihapus. Objek "setelah" adalah kebalikannya. Satu memiliki id dan yang lainnya tidak. Cara mereka ditampilkan mungkin berbeda. Daftar mereka muncul mungkin berbeda. Izin pengguna terkait mungkin berbeda. Keduanya adalah "laporan" dalam arti bahasa Inggris, tetapi keduanya sangat berbeda.Di sisi lain, kode Anda mungkin cukup sederhana sehingga satu objek akan cukup, tetapi itu sesuatu yang perlu dipertimbangkan jika kode Anda dibumbui
if (validId) {...} else {...}
.sumber
Tidak, itu tidak baik! Sangat cocok untuk prosedur untuk memodifikasi parameter hanya dalam bahasa prosedural, di mana tidak ada cara lain; dalam bahasa OOP panggilan metode perubahan pada objek, dalam hal ini pada laporan (seperti report.generateNewId ()).
Dalam hal ini metode Anda melakukan 2 hal, maka hancurkan SRP: ia memasukkan catatan dalam db dan menghasilkan id baru. Penelepon tidak dapat mengetahui bahwa metode Anda juga menghasilkan id baru karena hanya disebut insertRecord ().
sumber
db.Reports.InsertOnSubmit(report)
memanggil metode perubahan pada objek?