Prinsip ini didefinisikan sebagai modul yang memiliki satu alasan untuk berubah . Pertanyaan saya adalah, pasti alasan perubahan ini tidak diketahui sampai kode benar-benar mulai berubah ?? Cukup banyak setiap bagian kode memiliki banyak alasan mengapa itu bisa berubah tetapi pasti berusaha untuk mengantisipasi semua ini dan merancang kode Anda dengan pemikiran ini akan berakhir dengan kode yang sangat buruk. Bukankah ini ide yang lebih baik untuk benar-benar mulai menerapkan SRP ketika permintaan untuk mengubah kode mulai masuk? Lebih khusus lagi, ketika sepotong kode telah berubah lebih dari sekali karena lebih dari satu alasan, dengan demikian membuktikannya memiliki lebih dari satu alasan untuk berubah. Kedengarannya sangat anti-Agile untuk mencoba menebak alasan perubahan.
Contohnya adalah potongan kode yang mencetak dokumen. Permintaan masuk untuk mengubahnya untuk mencetak ke PDF dan kemudian permintaan kedua dibuat untuk mengubahnya untuk menerapkan beberapa format berbeda ke dokumen. Pada titik ini Anda memiliki bukti lebih dari satu alasan untuk berubah (dan melanggar SRP) dan harus membuat refactoring yang sesuai.
sumber
Jawaban:
Tentu saja, prinsip YAGNI akan memberitahu Anda untuk menerapkan SRP sebelum Anda benar-benar membutuhkannya. Tetapi pertanyaan yang harus Anda tanyakan pada diri sendiri adalah: apakah saya harus menerapkan SRP terlebih dahulu dan hanya ketika saya benar-benar harus mengubah kode saya?
Menurut pengalaman saya, penerapan SRP memberi Anda manfaat jauh lebih awal: ketika Anda harus mencari tahu di mana dan bagaimana menerapkan perubahan spesifik dalam kode Anda. Untuk tugas ini, Anda harus membaca dan memahami fungsi dan kelas yang ada. Ini menjadi jauh lebih mudah ketika semua fungsi dan kelas Anda memiliki tanggung jawab khusus. Jadi IMHO Anda harus menerapkan SRP setiap kali itu membuat kode Anda lebih mudah dibaca, setiap kali itu membuat fungsi Anda lebih kecil dan lebih menggambarkan diri. Jadi jawabannya adalah ya , masuk akal untuk menerapkan SRP bahkan untuk kode baru.
Misalnya, ketika kode pencetakan Anda membaca dokumen, memformat dokumen dan mencetak hasilnya ke perangkat tertentu, ini adalah 3 tanggung jawab yang jelas dapat dipisahkan. Jadi buat setidaknya 3 fungsi dari mereka, beri mereka sesuai nama. Sebagai contoh:
Sekarang, ketika Anda mendapatkan persyaratan baru untuk mengubah pemformatan dokumen atau yang lain untuk mencetak ke PDF, Anda tahu persis di mana dari fungsi atau lokasi ini dalam kode Anda harus menerapkan perubahan, dan yang lebih penting, di mana tidak.
Jadi, setiap kali Anda datang ke suatu fungsi Anda tidak mengerti karena fungsi itu "terlalu banyak", dan Anda tidak yakin apakah dan di mana harus menerapkan perubahan, maka pertimbangkan untuk refactor fungsi menjadi fungsi terpisah, lebih kecil. Jangan tunggu sampai Anda harus mengubah sesuatu. Kode 10x lebih sering dibaca daripada diubah, dan fungsi yang lebih kecil jauh lebih mudah dibaca. Menurut pengalaman saya, ketika suatu fungsi memiliki kompleksitas tertentu, Anda selalu dapat membagi fungsi menjadi tanggung jawab yang berbeda, terlepas dari mengetahui perubahan mana yang akan terjadi di masa depan. Bob Martin biasanya melangkah lebih jauh, lihat tautan yang saya berikan di komentar saya di bawah.
EDIT: untuk komentar Anda: Tanggung jawab utama dari fungsi luar dalam contoh di atas adalah tidak mencetak ke perangkat tertentu, atau untuk memformat dokumen - itu adalah untuk mengintegrasikan alur kerja pencetakan . Dengan demikian, pada tingkat abstraksi fungsi luar, persyaratan baru seperti "dokumen tidak boleh diformat lagi" atau "dokumen harus dikirim bukan dicetak" hanya "alasan yang sama" - yaitu "alur kerja pencetakan telah berubah". Jika kita berbicara tentang hal-hal seperti itu, penting untuk tetap berpegang pada tingkat abstraksi yang tepat .
sumber
Saya pikir Anda salah paham SRP.
Satu-satunya alasan untuk berubah BUKAN tentang mengubah kode tetapi tentang apa yang kode Anda lakukan.
sumber
Saya pikir definisi SRP sebagai "memiliki satu alasan untuk berubah" menyesatkan karena alasan ini. Ambillah persis pada nilai nominal: Prinsip Tanggung Jawab Tunggal mengatakan sebuah kelas atau fungsi harus memiliki satu tanggung jawab. Hanya memiliki satu alasan untuk berubah adalah efek samping dari hanya melakukan satu hal untuk memulai. Tidak ada alasan Anda setidaknya tidak dapat melakukan upaya terhadap tanggung jawab tunggal dalam kode Anda tanpa mengetahui apa pun tentang bagaimana hal itu dapat berubah di masa depan.
Salah satu petunjuk terbaik untuk hal semacam ini adalah ketika Anda memilih nama kelas atau fungsi. Jika tidak segera jelas apa nama kelas yang harus dinamai, atau namanya panjang / kompleks, atau nama tersebut menggunakan istilah umum seperti "manajer" atau "utilitas", maka itu mungkin melanggar SRP. Demikian pula, ketika mendokumentasikan API, itu akan menjadi jelas jika Anda melanggar SRP berdasarkan fungsionalitas apa yang Anda gambarkan.
Tentu saja ada nuansa SRP yang tidak dapat Anda ketahui sampai nanti dalam proyek - yang tampak seperti satu tanggung jawab ternyata dua atau tiga. Ini adalah kasus di mana Anda harus refactor untuk menerapkan SRP. Tetapi itu tidak berarti bahwa SRP harus diabaikan sampai Anda mendapatkan permintaan perubahan; yang mengalahkan tujuan SRP!
Untuk berbicara langsung dengan contoh Anda, pertimbangkan untuk mendokumentasikan metode cetak Anda. Jika Anda akan mengatakan "metode ini membentuk data untuk mencetak dan mengirimkannya ke printer," yang dan adalah apa yang membuat Anda: itu bukan tanggung jawab tunggal, itu dua tanggung jawab: format dan mengirim ke printer. Jika Anda mengenali ini dan membaginya menjadi dua fungsi / kelas, maka ketika permintaan perubahan Anda datang, Anda hanya akan memiliki satu alasan untuk setiap bagian untuk berubah.
sumber
Saya sudah berkali-kali menembak diri sendiri dengan menghabiskan terlalu banyak waktu mengadaptasi kode untuk mengakomodasi perubahan itu. Alih-alih hanya mencetak PDF bodoh itu.
Refactor Untuk Mengurangi Kode
Pola penggunaan tunggal dapat membuat kode mengasapi. Di mana paket dicemari dengan kelas spesifik kecil yang membuat tumpukan kode yang tidak masuk akal secara individual. Anda harus membuka lusinan file sumber hanya untuk memahami bagaimana ia sampai ke bagian pencetakan. Selain itu, bisa ada ratusan jika tidak ribuan baris kode yang ada hanya untuk mengeksekusi 10 baris kode yang melakukan pencetakan yang sebenarnya.
Buat A Bullseye
Pola penggunaan tunggal dimaksudkan untuk mengurangi kode sumber dan meningkatkan penggunaan kembali kode. Itu dimaksudkan untuk membuat spesialisasi dan implementasi spesifik. Semacam
bullseye
dalam kode sumber untuk Andago to specific tasks
. Ketika ada masalah dengan pencetakan, Anda tahu persis ke mana harus memperbaikinya.Penggunaan Tunggal Tidak Berarti Fraktur Ber ambigu
Ya, Anda memiliki kode yang sudah mencetak dokumen. Ya, Anda sekarang harus mengubah kode untuk juga mencetak PDF. Ya, Anda sekarang harus mengubah format dokumen.
Apakah Anda yakin
usage
telah berubah secara signifikan?Jika refactoring menyebabkan bagian dari kode sumber menjadi terlalu digeneralisasi. Sampai-sampai maksud asli dari
printing stuff
tidak lagi eksplisit, maka Anda telah membuat fraktur ambigu dalam kode sumber.Selalu pertahankan kode sumber Anda di organisasi yang paling mudah dipahami.
Jangan Menjadi Pembuat Jam
Sudah terlalu sering saya melihat pengembang memakai eyepiece, dan fokus pada detail kecil sampai-sampai tidak ada orang lain yang bisa menyatukan kembali potongan-potongan itu jika berantakan.
sumber
Alasan perubahan adalah, pada akhirnya, perubahan dalam spesifikasi atau informasi tentang lingkungan tempat aplikasi berjalan. Jadi satu prinsip tanggung jawab memberitahu Anda untuk menulis setiap komponen (kelas, fungsi, modul, layanan ...) sehingga perlu mempertimbangkan sesedikit mungkin spesifikasi dan lingkungan eksekusi.
Karena Anda mengetahui spesifikasi dan lingkungan ketika Anda menulis komponen, Anda dapat menerapkan prinsip tersebut.
Jika Anda mempertimbangkan contoh kode yang mencetak dokumen. Anda harus mempertimbangkan apakah Anda dapat menentukan templat tata letak tanpa mempertimbangkan dokumen akan berakhir dalam PDF. Anda bisa, jadi SRP memberi tahu Anda bahwa Anda harus.
Tentu saja YAGNI memberi tahu Anda bahwa Anda seharusnya tidak. Anda perlu menemukan keseimbangan antara prinsip-prinsip desain.
sumber
Flup menuju ke arah yang benar. "Prinsip tanggung jawab tunggal" awalnya diterapkan pada prosedur. Sebagai contoh, Dennis Ritchie akan mengatakan bahwa suatu fungsi harus melakukan satu hal dan melakukannya dengan baik. Kemudian, dalam C ++, Bjarne Stroustrup akan mengatakan bahwa sebuah kelas harus melakukan satu hal dan melakukannya dengan baik.
Perhatikan bahwa, kecuali sebagai patokan, keduanya secara formal memiliki sedikit atau tidak ada hubungannya satu sama lain. Mereka hanya melayani apa yang nyaman untuk diekspresikan dalam bahasa pemrograman. Ya, itu sesuatu. Tapi itu cerita yang sangat berbeda dari apa yang mendorong flup.
Implementasi modern (yaitu, tangkas dan DDD) lebih fokus pada apa yang penting bagi bisnis daripada pada apa yang bisa diungkapkan oleh bahasa pemrograman. Bagian yang mengejutkan adalah bahwa bahasa pemrograman belum menyusul. Bahasa FORTRAN seperti menangkap tanggung jawab yang sesuai dengan model konseptual utama saat itu: proses yang diterapkan pada masing-masing kartu saat melewati pembaca kartu, atau (seperti dalam C) pemrosesan yang menyertai setiap interupsi. Kemudian muncul bahasa ADT, yang telah matang untuk menangkap apa yang kemudian ditemukan kembali oleh orang-orang DDD sebagai penting (meskipun Jim Neighbours sebagian besar sudah tahu, diterbitkan, dan digunakan pada 1968): apa yang sekarang kita sebut kelas . (Mereka BUKAN modul.)
Langkah ini kurang evolusi daripada ayunan pendulum. Saat pendulum berayun ke data kami kehilangan pemodelan use case yang melekat dalam FORTRAN. Tidak masalah ketika fokus utama Anda melibatkan data atau bentuk pada layar. Ini adalah model yang bagus untuk program seperti PowerPoint, atau setidaknya untuk operasi sederhana.
Apa yang hilang adalah tanggung jawab sistem . Kami tidak menjual elemen-elemen DDD. Dan kami tidak menggunakan metode kelas dengan baik. Kami menjual tanggung jawab sistem. Pada tingkat tertentu, Anda perlu merancang sistem Anda berdasarkan prinsip tanggung jawab tunggal.
Jadi jika Anda melihat orang-orang seperti Rebecca Wirfs-Brock, atau saya, yang dulu berbicara tentang metode kelas, kita sekarang berbicara dalam hal kasus penggunaan. Itu yang kami jual. Itu adalah operasi sistem. Kasus penggunaan harus memiliki satu tanggung jawab. Kasus penggunaan jarang merupakan unit arsitektur. Tapi semua orang berusaha berpura-pura begitu. Saksikan orang-orang SOA, misalnya.
Inilah sebabnya saya senang tentang arsitektur DCI Trygve Reenskaug - yang dijelaskan dalam buku Arsitektur Lean di atas. Ini akhirnya memberikan kedudukan nyata pada apa yang dulunya merupakan pengabdian yang sewenang-wenang dan mistis terhadap "tanggung jawab tunggal" - seperti yang ditemukan dalam sebagian besar argumen di atas. Perawakannya berhubungan dengan model mental manusia: pengguna akhir pertama dan programmer kedua. Ini berkaitan dengan masalah bisnis. Dan, hampir secara kebetulan, itu merangkum perubahan sebagai tantangan menantang kita.
Prinsip tanggung jawab tunggal seperti yang kita tahu adalah dinosaurus yang tersisa dari masa asalnya atau kuda hobi yang kita gunakan sebagai pengganti pemahaman. Anda perlu meninggalkan beberapa kuda hobi ini untuk melakukan perangkat lunak yang hebat. Dan itu membutuhkan pemikiran di luar kotak. Menjaga segala sesuatunya sederhana dan mudah dipahami hanya akan berhasil bila masalahnya sederhana dan mudah dipahami. Saya tidak terlalu tertarik dengan solusi-solusi itu: mereka tidak tipikal, dan bukan di mana tantangannya.
sumber
Ya, Prinsip Tanggung Jawab-Tunggal harus diterapkan pada kode baru.
Tapi! Apa itu tanggung jawab?
Apakah "mencetak laporan merupakan tanggung jawab"? Jawabannya, saya percaya adalah "Mungkin."
Mari kita coba menggunakan definisi SRP sebagai "hanya memiliki satu alasan untuk berubah".
Misalkan Anda memiliki fungsi yang mencetak laporan. Jika Anda memiliki dua perubahan:
Maka perubahan pertama adalah "ubah gaya laporan" yang lain adalah "ubah format output laporan" dan sekarang Anda harus meletakkannya dalam dua fungsi yang berbeda karena ini adalah hal yang berbeda.
Tetapi jika perubahan kedua Anda adalah:
2b. ubah fungsi itu karena laporan Anda memerlukan font yang berbeda
Saya akan mengatakan kedua perubahan adalah "mengubah gaya laporan" dan mereka dapat tetap dalam satu fungsi.
Jadi, di mana itu meninggalkan kita? Seperti biasa, Anda harus mencoba untuk menjaga hal-hal sederhana dan mudah dimengerti. Jika mengubah warna latar belakang berarti 20 baris kode dan mengubah font berarti 20 baris kode, buat dua fungsi lagi. Jika masing-masing satu baris, simpan dalam satu baris.
sumber
Saat Anda merancang sistem baru, sebaiknya mempertimbangkan jenis perubahan yang mungkin harus Anda buat selama masa pakainya dan seberapa mahal mereka akan diberikan arsitektur yang Anda tempatkan. Membagi sistem Anda menjadi modul adalah keputusan mahal untuk membuat kesalahan.
Sumber informasi yang baik adalah model mental di kepala pakar domain bisnis. Ambil contoh dokumen, format, dan pdf. Pakar domain kemungkinan akan memberi tahu Anda bahwa mereka memformat surat mereka menggunakan templat dokumen. Baik di stasioner atau di Word atau apa pun. Anda dapat mengambil informasi ini sebelum mulai membuat kode dan menggunakannya dalam desain Anda.
Bacaan yang bagus tentang hal-hal ini: Lean Architecture oleh Coplien
sumber
"Cetak" sangat mirip "lihat" di MVC. Siapa pun yang memahami dasar-dasar objek akan memahami itu.
Ini adalah tanggung jawab sistem . Ini diimplementasikan sebagai mekanisme - MVC - yang melibatkan printer (View), hal yang sedang dicetak (Module) dan permintaan dan opsi printer (dari Controller).
Berusaha melokalisasi ini sebagai tanggung jawab kelas atau modul sama saja dan mencerminkan pemikiran 30 tahun. Kami telah belajar banyak sejak saat itu, dan itu cukup terbukti dalam literatur dan kode programmer yang matang.
sumber
Idealnya, Anda sudah memiliki ide bagus tentang tanggung jawab berbagai bagian kode. Membagi menjadi tanggung jawab berdasarkan insting pertama Anda, mungkin dengan mempertimbangkan apa yang ingin dilakukan oleh perpustakaan yang Anda gunakan (mendelegasikan tugas, tanggung jawab, ke perpustakaan biasanya merupakan hal yang hebat untuk dilakukan, asalkan perpustakaan dapat benar-benar melakukan tugas tersebut. ). Kemudian, perbaiki pemahaman Anda tentang tanggung jawab sesuai dengan perubahan persyaratan. Semakin baik Anda memahami sistem pada awalnya, semakin sedikit Anda perlu mengubah penugasan tanggung jawab secara mendasar (meskipun kadang-kadang Anda menemukan bahwa suatu tanggung jawab lebih baik dibagi menjadi sub-tanggung jawab).
Bukan berarti Anda harus menghabiskan waktu lama untuk mengkhawatirkannya. Fitur utama kode adalah dapat diubah kemudian, Anda tidak harus membuatnya sepenuhnya benar saat pertama kali. Cobalah untuk menjadi lebih baik dari waktu ke waktu dalam mempelajari bentuk tanggung jawab apa yang dimiliki sehingga Anda dapat membuat lebih sedikit kesalahan di masa depan.
Ini hanyalah indikasi bahwa tanggung jawab keseluruhan - “mencetak” kode - memiliki sub-tanggung jawab, dan harus dipecah menjadi beberapa bagian. Itu bukan pelanggaran SRP per se tetapi lebih merupakan indikasi bahwa partisi (mungkin menjadi sub-tugas "format" dan "rendering") mungkin diperlukan. Dapatkah Anda menggambarkan tanggung jawab itu dengan jelas sehingga Anda dapat memahami apa yang terjadi dalam sub-tugas tanpa melihat implementasinya? Jika Anda bisa, itu mungkin perpecahan yang masuk akal.
Mungkin juga lebih jelas jika kita melihat contoh nyata yang sederhana. Mari kita pertimbangkan
sort()
metode utilitas dijava.util.Arrays
. Apa fungsinya? Ini semacam array, dan hanya itu yang dilakukannya. Itu tidak mencetak elemen, tidak menemukan anggota yang paling cocok secara moral, tidak bersiul Dixie . Itu hanya semacam array.Anda juga tidak perlu tahu caranya. Penyortiran adalah satu-satunya tanggung jawab metode itu. (Sebenarnya, ada banyak metode penyortiran di Jawa untuk alasan teknis yang agak jelek untuk dilakukan dengan tipe primitif; Anda tidak harus memperhatikan hal itu, karena semuanya memiliki tanggung jawab yang setara.)Buat metode Anda, kelas Anda, modul Anda, buat mereka memiliki peran yang ditentukan dengan jelas dalam kehidupan. Itu membuat jumlah yang harus Anda pahami sekaligus turun, dan pada gilirannya adalah apa yang memungkinkan Anda untuk menangani merancang dan memelihara sistem yang besar.
sumber