Hari ini saya bertengkar dengan seseorang.
Saya sedang menjelaskan manfaat memiliki model domain yang kaya dibandingkan dengan model domain yang anemia. Dan saya menunjukkan poin saya dengan kelas sederhana yang terlihat seperti itu:
public class Employee
{
public Employee(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastname;
}
public string FirstName { get private set; }
public string LastName { get; private set;}
public int CountPaidDaysOffGranted { get; private set;}
public void AddPaidDaysOffGranted(int numberOfdays)
{
// Do stuff
}
}
Saat dia membela pendekatan model anemianya, salah satu argumennya adalah: "Saya seorang yang percaya pada SOLID . Anda melanggar prinsip tanggung jawab tunggal (SRP) karena Anda berdua mewakili data dan melakukan logika di kelas yang sama."
Saya menemukan klaim ini benar-benar mengejutkan, karena mengikuti alasan ini, setiap kelas yang memiliki satu properti dan satu metode melanggar SRP, dan karena itu OOP secara umum tidak SOLID, dan pemrograman fungsional adalah satu-satunya jalan ke surga.
Saya memutuskan untuk tidak menjawab banyak argumennya, tetapi saya ingin tahu apa pendapat masyarakat tentang pertanyaan ini.
Jika saya menjawab, saya akan mulai dengan menunjuk pada paradoks yang disebutkan di atas, dan kemudian menunjukkan bahwa SRP sangat tergantung pada tingkat granularitas yang ingin Anda pertimbangkan dan bahwa jika Anda mengambilnya cukup jauh, setiap kelas yang berisi lebih dari satu properti atau satu metode melanggarnya.
Apa yang akan kamu katakan?
Pembaruan: Contoh ini telah dimutakhirkan dengan murah hati oleh guntbert untuk membuat metode ini lebih realistis dan membantu kami fokus pada diskusi yang mendasarinya.
sumber
Jawaban:
Tanggung jawab tunggal harus dipahami sebagai abstraksi tugas logis dalam sistem Anda. Kelas harus memiliki tanggung jawab tunggal untuk (melakukan semua yang diperlukan untuk) melakukan satu tugas tunggal. Ini benar-benar dapat membawa banyak ke kelas yang dirancang dengan baik, tergantung pada apa tanggung jawabnya. Kelas yang menjalankan mesin skrip Anda, misalnya, dapat memiliki banyak metode dan data yang terlibat dalam pemrosesan skrip.
Rekan kerja Anda berfokus pada hal yang salah. Pertanyaannya bukan "anggota apa yang dimiliki kelas ini?" tetapi "operasi bermanfaat apa yang dilakukan kelas ini dalam program?" Setelah itu dipahami, model domain Anda terlihat baik-baik saja.
sumber
Prinsip Tanggung Jawab Tunggal hanya peduli apakah sepotong kode (dalam OOP, biasanya kita berbicara tentang kelas) memiliki tanggung jawab atas satu fungsi . Saya pikir teman Anda mengatakan bahwa fungsi dan data tidak dapat digabungkan, tidak benar-benar memahami gagasan itu. Jika
Employee
juga berisi informasi tentang tempat kerjanya, seberapa cepat mobilnya berjalan, dan jenis makanan apa yang dimakan anjingnya maka kita akan mengalami masalah.Karena kelas ini hanya berurusan dengan
Employee
, saya pikir itu adil untuk mengatakan itu tidak melanggar SRP terang-terangan, tetapi orang selalu akan memiliki pendapat mereka sendiri.Satu tempat di mana kita dapat meningkatkan adalah untuk memisahkan informasi Karyawan (seperti nama, nomor telepon, email) dari liburannya. Dalam pikiran saya, ini tidak berarti bahwa metode dan data tidak dapat saling berbaur , itu hanya berarti bahwa mungkin fungsi liburan bisa berada di tempat yang terpisah.
sumber
Menurut saya kelas ini berpotensi melanggar SRP jika terus mewakili
Employee
danEmployeeHolidays
.Seperti itu, dan jika itu datang kepada saya untuk Peer Review, saya mungkin akan membiarkannya. Jika lebih banyak properti dan metode spesifik Karyawan ditambahkan, dan lebih banyak properti khusus liburan ditambahkan, saya mungkin akan menyarankan pemisahan, mengutip SRP dan ISP.
sumber
Sudah ada jawaban bagus yang menunjukkan bahwa SRP adalah konsep abstrak tentang fungsi logis, tetapi ada poin yang lebih halus yang menurut saya layak ditambahkan.
Dua huruf pertama dalam SOLID, SRP dan OCP, keduanya tentang bagaimana kode Anda berubah sebagai respons terhadap perubahan persyaratan. Definisi favorit saya tentang SRP adalah: "modul / kelas / fungsi seharusnya hanya memiliki satu alasan untuk berubah." Berdebat tentang kemungkinan alasan untuk mengubah kode Anda jauh lebih produktif daripada berdebat tentang apakah kode Anda SOLID atau tidak.
Berapa banyak alasan yang harus diubah oleh kelas Karyawan Anda? Saya tidak tahu, karena saya tidak tahu konteks di mana Anda menggunakannya, dan saya juga tidak bisa melihat masa depan. Yang bisa saya lakukan adalah bertukar pikiran tentang kemungkinan perubahan berdasarkan apa yang saya lihat di masa lalu, dan Anda dapat menilai secara subjektif seberapa besar kemungkinannya. Jika lebih dari satu skor antara "kemungkinan masuk akal" dan "kode saya telah berubah karena alasan yang tepat", maka Anda melanggar SRP terhadap perubahan semacam itu. Ini satu: seseorang dengan lebih dari dua nama bergabung dengan perusahaan Anda (atau seorang arsitek membaca artikel W3C yang luar biasa ini ). Ini satu lagi: perusahaan Anda mengubah cara mengalokasikan hari libur.
Perhatikan bahwa alasan ini sama-sama valid bahkan jika Anda menghapus metode AddHolidays. Banyak model domain anemia yang melanggar SRP. Banyak dari mereka hanya database-tables-in-code, dan sangat umum untuk tabel database memiliki 20 alasan untuk berubah.
Ini adalah sesuatu untuk dikunyah: apakah kelas Karyawan Anda akan berubah jika sistem Anda perlu melacak gaji karyawan? Alamat? Info kontak darurat? Jika Anda mengatakan "ya" (dan "kemungkinan akan terjadi") pada dua dari itu, maka kelas Anda akan melanggar SRP bahkan jika belum ada kode di dalamnya! SOLID adalah tentang proses dan arsitektur sebanyak ini tentang kode.
sumber
Bahwa kelas mewakili data bukan tanggung jawab kelas, itu adalah detail implementasi pribadi.
Kelas memiliki satu tanggung jawab, untuk mewakili seorang karyawan. Dalam konteks ini, itu berarti menyajikan beberapa API publik yang memberi Anda fungsionalitas yang Anda butuhkan untuk berurusan dengan karyawan (apakah AddHolidays adalah contoh yang baik masih bisa diperdebatkan).
Implementasinya internal; Kebetulan ini membutuhkan beberapa variabel pribadi dan beberapa logika. Itu tidak berarti bahwa kelas sekarang memiliki banyak tanggung jawab.
sumber
Gagasan bahwa mencampur logika dan data dengan cara apa pun selalu salah, sangat konyol bahkan tidak pantas didiskusikan. Namun, memang ada pelanggaran yang jelas dari tanggung jawab tunggal dalam contoh, tetapi itu bukan karena ada properti
DaysOfHolidays
dan fungsiAddHolidays(int)
.Itu karena identitas karyawan bercampur dengan manajemen liburan, yang buruk. Identitas karyawan adalah hal kompleks yang diperlukan untuk melacak liburan, gaji, lembur, untuk mewakili siapa di tim mana, untuk menautkan ke laporan kinerja, dll. Seorang karyawan juga dapat mengubah nama depan, nama belakang, atau keduanya, dan tetap sama karyawan. Karyawan bahkan mungkin memiliki beberapa ejaan nama mereka seperti ASCII dan ejaan unicode. Orang dapat memiliki 0 hingga n nama depan dan / atau belakang. Mereka mungkin memiliki nama yang berbeda di yurisdiksi yang berbeda. Melacak identitas karyawan adalah cukup tanggung jawab sehingga manajemen liburan atau liburan tidak dapat ditambahkan tanpa menyebutnya tanggung jawab kedua.
sumber
Seperti yang lain, saya tidak setuju dengan ini.
Saya akan mengatakan bahwa SRP dilanggar jika Anda melakukan lebih dari satu bagian logika di kelas. Berapa banyak data yang perlu disimpan dalam kelas untuk mencapai satu potongan logika itu tidak relevan.
sumber
Saya tidak merasa berguna akhir-akhir ini untuk berdebat tentang apa yang dilakukan dan tidak merupakan tanggung jawab tunggal atau satu alasan untuk berubah. Saya akan mengusulkan Prinsip Duka Minimum sebagai gantinya:
Bagaimana dengan itu? Seharusnya tidak mengambil ilmuwan roket untuk mencari tahu mengapa ini dapat membantu mengurangi biaya perawatan dan mudah-mudahan itu tidak menjadi titik perdebatan tanpa akhir, tetapi seperti dengan SOLID pada umumnya, itu bukan sesuatu untuk diterapkan secara membabi buta di mana-mana. Itu adalah sesuatu yang perlu dipertimbangkan sambil menyeimbangkan pertukaran.
Adapun kemungkinan membutuhkan perubahan, itu turun dengan:
Adapun kesulitan membuat perubahan, itu naik dengan kopling eferen. Pengujian memperkenalkan kopling eferen tetapi meningkatkan keandalan. Dilakukan dengan baik, umumnya lebih baik daripada merugikan dan benar-benar dapat diterima dan dipromosikan oleh Prinsip Duka Minimum.
Dan Prinsip Buat Badass terikat dengan Prinsip Duka Minimum, karena hal-hal badass akan menemukan kemungkinan lebih rendah untuk membutuhkan perubahan daripada hal-hal yang menyedot apa yang mereka lakukan.
Dari sudut pandang SRP, sebuah kelas yang nyaris tidak melakukan apa pun tentu hanya memiliki satu (kadang-kadang nol) alasan untuk berubah:
Itu praktis tidak memiliki alasan untuk berubah! Lebih baik dari SRP. Ini ZRP!
Kecuali saya sarankan itu adalah pelanggaran terang-terangan terhadap Prinsip Make Badass. Ini juga sama sekali tidak berharga. Sesuatu yang tidak begitu banyak berharap tidak bisa menjadi badass. Ini memiliki terlalu sedikit informasi (TLI). Dan tentu saja ketika Anda memiliki sesuatu yang TLI, ia tidak dapat melakukan sesuatu yang benar-benar bermakna, bahkan dengan informasi yang dirangkumnya, sehingga ia harus membocorkannya ke dunia luar dengan harapan bahwa orang lain benar-benar akan melakukan sesuatu yang bermakna dan sombong. Dan kebocoran itu oke untuk sesuatu yang hanya dimaksudkan untuk mengumpulkan data dan tidak lebih, tetapi ambang itu adalah perbedaan seperti yang saya lihat antara "data" dan "objek".
Tentu saja sesuatu yang merupakan TMI juga buruk. Kami mungkin menempatkan seluruh perangkat lunak kami dalam satu kelas. Bahkan dapat memiliki satu
run
metode. Dan seseorang bahkan mungkin berpendapat bahwa sekarang ada satu alasan yang sangat luas untuk berubah: "Kelas ini hanya perlu diubah jika perangkat lunak perlu ditingkatkan." Saya bodoh, tapi tentu saja kita bisa membayangkan semua masalah pemeliharaan dengan itu.Jadi ada tindakan keseimbangan untuk granularity atau kekasaran objek yang Anda desain. Saya sering menilai dengan seberapa banyak informasi yang Anda miliki bocor ke dunia luar, dan seberapa banyak fungsionalitas bermakna yang dapat dilakukan. Saya sering mendapati Prinsip Make Badass bermanfaat di sana untuk menemukan keseimbangan sambil menggabungkannya dengan Prinsip Duka Minimum.
sumber
Sebaliknya, bagi saya model domain Anemik mematahkan beberapa konsep utama OOP (yang menyatukan atribut dan perilaku), tetapi dapat dihindari berdasarkan pilihan arsitektur. Domain anemia lebih mudah dipikirkan, kurang organik, dan lebih berurutan.
Banyak sistem cenderung melakukan ini ketika banyak lapisan harus bermain dengan data yang sama (lapisan layanan, lapisan web, lapisan klien, agen ...).
Lebih mudah untuk mendefinisikan struktur data di satu tempat dan perilaku di kelas layanan lainnya. Jika kelas yang sama digunakan pada beberapa lapisan, yang satu ini dapat tumbuh besar, dan ia menanyakan pertanyaan lapisan mana yang bertanggung jawab untuk menentukan perilaku yang dibutuhkan, dan siapa yang dapat memanggil metode.
Sebagai contoh, itu mungkin bukan ide yang baik daripada proses Agen yang menghitung statistik pada semua karyawan Anda dapat memanggil menghitung untuk hari yang dibayar. Dan daftar karyawan GUI tentu saja tidak memerlukan perhitungan id agregat baru yang digunakan dalam agen statistik ini (dan data teknis yang menyertainya). Ketika Anda memisahkan metode dengan cara ini, Anda biasanya berakhir dengan kelas dengan hanya struktur data.
Anda dapat dengan mudah membuat serial / deserialize data "objek", atau hanya beberapa dari mereka, atau ke format lain (json) ... tanpa khawatir tentang konsep objek / tanggung jawab. Hanya data yang lewat. Anda selalu dapat melakukan pemetaan data antara dua kelas (Karyawan, EmployeeVO, EmployeeStatistic ...) tetapi apa yang dimaksud dengan Karyawan di sini?
Jadi ya itu benar-benar memisahkan data dalam kelas domain dan penanganan data dalam kelas layanan tetapi di sini diperlukan. Sistem seperti itu sekaligus fungsional untuk membawa nilai bisnis dan teknis juga untuk menyebarkan data ke mana pun dibutuhkan, sambil menjaga ruang lingkup tanggung jawab yang tepat (dan objek yang didistribusikan juga tidak menyelesaikan masalah ini).
Jika Anda tidak perlu memisahkan lingkup perilaku Anda lebih bebas untuk meletakkan metode di kelas layanan atau di kelas domain, tergantung pada bagaimana Anda melihat objek Anda. Saya cenderung melihat objek sebagai konsep "nyata", ini secara alami membantu menjaga SRP. Jadi, dalam contoh Anda, itu lebih realistis daripada Bos Karyawan menambahkan hari gajian diberikan ke PayDayAccount-nya. Seorang Karyawan Dipekerjakan oleh perusahaan, Pekerjaan, bisa Sakit atau Diminta saran, dan dia memiliki akun Payday (Bos dapat mengambilnya langsung dari dia atau dari registry PayDayAccount ...) Tetapi Anda dapat membuat pintasan teragregasi di sini untuk kesederhanaan jika Anda tidak ingin terlalu banyak kerumitan untuk perangkat lunak sederhana.
sumber
Kedengarannya sangat masuk akal bagi saya. Model mungkin tidak memiliki properti publik jika mengekspos tindakan. Ini pada dasarnya ide Pemisahan Command-Query. Harap dicatat bahwa Command akan memiliki negara pribadi pasti.
sumber
Anda tidak dapat melanggar Prinsip Tanggung Jawab Tunggal karena itu hanya kriteria estetika, bukan aturan Alam. Jangan disesatkan oleh nama yang terdengar ilmiah dan huruf besar.
sumber