Saya baru-baru ini berdebat dengan pengembang lain mengenai kelas di bawah ini:
public class GroupBillingPayment
{
public void Save(IGroupBillingPayment model)
{
if (model == null || UserInfo.UserID == 0)
{
throw new Exception("GroupBillingPayment object or Current User Id is NULL , Please Contact Administrator.");
}
Data.GroupBillingPayment groupBillingPayment = RepositoryManager.GroupBillingPaymentRepository.GetById(model.GroupBillingPaymentID);
Mapper.Map(model, groupBillingPayment);
ServiceManager.GroupBilling.IsBillAlreadyCancelled(groupBillingPayment.GroupBillingID, THROW_ERROR);
groupBillingPayment.UpdatedBy = UserInfo.UserID;
groupBillingPayment.UpdatedOn = DateTime.Now;
RepositoryManager.GroupBillingPaymentRepository.Update(groupBillingPayment, false);
UpdateGroupBilling([Parameters])
}
}
Saya percaya UpdateGroupBilling
tidak boleh dipanggil di dalam metode save karena melanggar prinsip tanggung jawab tunggal. Namun, ia mengatakan bahwa setiap kali pembayaran dilakukan, tagihan harus diperbarui. Karenanya inilah pendekatan yang tepat.
Pertanyaan saya, apakah SRP dilanggar di sini? Jika ya, bagaimana kita bisa memperbaiki itu agar kedua kriteria kita terpenuhi?
Jawaban:
Saya akan melihatnya seperti ini:
Suatu metode harus memanggil metode lain (dalam objek yang sama atau lainnya) yang membuatnya menjadi metode yang dikomposisikan atau melakukan "perhitungan primitif" (tingkat abstraksi yang sama).
Tanggung jawab metode yang dikomposisikan adalah "panggil metode lain".
Jadi jika
Save
metode Anda melakukan beberapa "perhitungan primitif" itu sendiri (mis. Memeriksa nilai pengembalian), SPR mungkin dilanggar. Jika "perhitungan primitif" ini hidup dalam metode lain yang disebut denganSave
metode Anda SRP tidak dilanggar.Versi terbaru dari
Save
metode ini tidak mengikuti prinsip lapisan abstraksi tunggal . Karena ini adalah masalah yang lebih penting, Anda harus mengekstraknya ke metode baru.Ini akan dikonversi
Save
menjadi metode yang dikomposisikan . Seperti yang tertulis, tanggung jawab metode yang dikomposisikan adalah "panggil metode lain". Oleh karena itu menelepon diUpdateGroupBilling([Parameters])
sini bukan merupakan pelanggaran terhadap SRP, tetapi keputusan kasus bisnis.sumber
Save
metode telah diperbarui. Silakan lihat apakah ini membantuTanggung jawab tunggal dapat diartikan sebagai fungsi / kelas harus hanya memiliki satu alasan untuk berubah.
Jadi
Save
metode Anda saat ini akan melanggar prinsip itu, karena itu harus diubah oleh lebih dari satu alasan.1. Simpan logika berubah
2. Jika Anda memutuskan untuk memperbarui sesuatu yang lain selain memperbarui grup penagihan
Anda dapat refactor dengan memperkenalkan
SaveModel
metode yang hanya akan bertanggung jawab untuk menyimpan. Dan memperkenalkan metode lain yang menggabungkan semua operasi yang diperlukan berdasarkan kebutuhan Anda. Jadi Anda berakhir dengan dua metodeDi mana
SaveModel
metode akan memiliki tanggung jawab untuk menyimpan model ke database dan satu alasan untuk berubah - jika menyimpan "logika" akan berubah.Save
metode sekarang memiliki tanggung jawab untuk memanggil semua metode yang diperlukan untuk proses "tabungan" penuh, dan memiliki satu alasan untuk berubah - jika jumlah metode yang diperlukan akan berubah.Saya pikir validasi dapat dipindahkan ke luar
SaveModel
juga.sumber
Single Responsibility adalah IMHO konsep yang tidak mudah untuk dipecahkan.
Aturan praktis yang sederhana adalah:
Di satu sisi, itu hanya indikator dan di sisi lain itu bekerja lebih baik terbalik: Jika dua hal terjadi, Anda tidak dapat menghindari menggunakan kata »dan« dan karena Anda melakukan dua hal, Anda melanggar SRP .
Tetapi, apakah itu berarti di sisi lain, jika Anda melakukan lebih dari satu hal, Anda melanggar SRP ? Tidak. Karena jika tidak, Anda terbatas pada kode sepele dan masalah sepele untuk dipecahkan. Anda akan melukai diri sendiri jika interpretasi Anda terlalu ketat.
Perspektif lain tentang SRP adalah: satu tingkat abstraksi . Selama Anda berurusan dengan satu tingkat abstraksi, Anda sebagian besar baik-baik saja.
Apa arti semua itu untuk pertanyaan Anda:
Untuk memutuskan, apakah ini merupakan pelanggaran terhadap SRP , perlu diketahui, apa yang terjadi di
save()
-Metode. Jika metode ini -seperti nama-menyarankan bertanggung jawab untuk bertahan model untuk database, panggilan untukUpdateGroupBilling
adalah IMHO pelanggaran SRP , karena Anda memperluas konteks »menghemat pembayaran«. Anda akan memparafrasekannya dengan »Saya menyimpan pembayaran dan memperbarui tagihan grup«, yang - seperti yang saya katakan sebelumnya - merupakan indikator (kemungkinan) pelanggaran SRP .Di sisi lain, jika metode ini menggambarkan "resep-pembayaran" -yaitu langkah-langkah apa yang harus diambil dalam proses- dan memutuskan: setiap kali pembayaran selesai, itu harus disimpan (ditangani di tempat lain) dan kemudian penagihan grup harus diperbarui (ditangani di tempat lain), itu bukan pelanggaran SRP, karena Anda tidak meninggalkan abstraksi "resep": Anda membedakan antara apa yang harus dilakukan (dalam resep) dan di mana itu dilakukan (di kelas / metode / modul yang sesuai). Tetapi jika itu yang dilakukan oleh
save()
-metode Anda (menjelaskan langkah mana yang harus diambil), Anda harus mengganti nama.Tanpa konteks lebih lanjut, sulit untuk mengatakan sesuatu yang konkret.
Edit: Anda memperbarui pos inital Anda. Saya akan mengatakan, bahwa metode ini melanggar SRP dan harus di refactored. The pengambilan data harus menjadi faktor keluar (itu harus menjadi parameter dari metode ini). The penambahan data (updateBy / On) harus dilakukan di tempat lain. The penghematan harus dilakukan di tempat lain. Maka akan baik-baik saja untuk pergi
UpdateGroupBilling([Parameters])
apa adanya.sumber
Sulit untuk memastikan tanpa konteks penuh tetapi saya pikir intuisi Anda benar. Indikator SRP favorit pribadi saya adalah apakah Anda akan tahu persis ke mana harus pergi dan mengubah sesuatu untuk memodifikasi fitur beberapa bulan kemudian.
Kelas yang mendefinisikan jenis pembayaran bukanlah tempat yang saya harapkan untuk mendefinisikan kembali siapa yang membayar atau bagaimana pembayaran dilakukan. Saya berharap itu menjadi salah satu dari banyak jenis pembayaran yang hanya memberikan detail penting yang digunakan beberapa bagian lain dari aplikasi untuk memulai, melakukan, atau memutar kembali / membatalkan transaksi, mengumpulkan detail tersebut melalui antarmuka yang seragam.
Ini juga terlihat seperti resep untuk pelanggaran prinsip KERING yang lebih umum jika Anda akan memiliki beberapa jenis masing-masing memilah transaksi mereka sendiri menggunakan banyak metode yang sama. SOLID tidak selalu merasa 100% relevan dengan pengembang JavaScript (saya kira banyak hal itu dijelaskan dengan buruk), tetapi KERING adalah standar utama dalam metodologi pemrograman umum IMO. Setiap kali saya menemukan ide yang terasa seperti perpanjangan KERING, saya memberikannya pertimbangan. SRP adalah salah satunya. Jika semua orang tetap pada topik, Anda cenderung tergoda untuk mengulangi sendiri.
sumber
Jika hanya ada satu cara untuk secara konseptual
UpdateGroupBilling(...)
, dan itu hanya pernah terjadi di tempat itu, maka mungkin tidak masalah di mana itu berada. Tetapi pertanyaannya terkait dengan konsep, bukan dengan keadaan duniawi, yaitu "untuk saat ini kita hanya melakukannya di sini."Jika tidak demikian, maka salah satu cara untuk memperbaikinya adalah dengan menggunakan Terbitkan / Berlangganan dan beri tahu setiap kali pembayaran disimpan, dan minta penagihan berlangganan acara itu dan perbarui entri yang relevan. Ini bagus terutama jika Anda perlu memiliki beberapa jenis penagihan yang perlu diperbarui. Metode lain adalah dengan memikirkan Penagihan sebagai pola strategi, yaitu memilih satu dan menggunakannya, atau menerimanya serta parameter. Atau, jika penagihan tidak selalu terjadi, Anda dapat menambahkannya sebagai dekorator, dll, dll. Tetapi sekali lagi, ini tergantung pada model konseptual Anda dan bagaimana Anda ingin memikirkannya, jadi Anda harus mencari tahu .
Namun, satu hal penting yang perlu dipertimbangkan adalah penanganan kesalahan. Jika penagihan gagal, apa yang terjadi dengan operasi sebelumnya?
sumber
Saya pikir desain ini melanggar SRP, tetapi sangat mudah untuk memperbaikinya dan masih melakukan segala hal lain yang diperlukan.
Pikirkan tentang pesan dan tentang apa yang terjadi dalam metode ini. Seharusnya menyimpan
GroupBillingPayment
, tetapi tidak ada yang salah jikaGroupBillingPayment
memberi tahu kelas bahwa itu telah disimpan. Terutama jika menerapkan pola yang secara eksplisit memperlihatkan perilaku itu.Anda bisa menggunakan pola Pengamat .
Berikut ini contoh cara kerjanya dalam kasus Anda:
Sekarang yang harus Anda lakukan adalah membuat satu atau lebih
Observers
yang akan terikatGroupBillingPayment
dan dengan demikian diberitahu setiap kali itu disimpan. Itu membuat dependensinya sendiri, tidak tahu apa yang diberitahukan sehingga tidak bergantung pada ini sama sekali.sumber
Anda ingin mencapai X. Kecuali X cukup sepele, Anda menulis metode yang akan melakukan segalanya untuk mencapai X, panggil metode "reachX", dan panggil metode itu. Tanggung jawab tunggal "mencapai X" adalah melakukan segalanya untuk mencapai X. "Mencapai X" tidak boleh melakukan hal lain.
Jika X rumit, maka banyak tindakan mungkin diperlukan untuk mencapai X. Serangkaian tindakan yang diperlukan dapat berubah dari waktu ke waktu, jadi ketika itu terjadi, Anda mengubah metode mencapai X, tetapi jika semuanya berjalan dengan baik, Anda masih bisa menyebutnya begitu.
Dalam contoh Anda, metode "Simpan" tanggung jawab bukan untuk menyimpan tagihan dan memperbarui tagihan grup (dua tanggung jawab), tanggung jawabnya adalah untuk mengambil semua tindakan yang diperlukan untuk membuat tagihan dicatat secara permanen. Satu tanggung jawab. Mungkin Anda menamainya dengan buruk.
sumber
Jika saya mengerti maksud Anda, saya akan memiliki pembayaran untuk meningkatkan acara seperti "Pembayaran dilakukan" dan kelas yang menangani penagihan yang berlangganan. Dengan cara ini penangan pembayaran tidak tahu apa-apa tentang penagihan.
sumber