Saat menggunakan Prinsip Tanggung Jawab Tunggal, apa yang merupakan "tanggung jawab?"

198

Tampaknya cukup jelas bahwa "Prinsip Tanggung Jawab Tunggal" tidak berarti "hanya melakukan satu hal." Itulah gunanya metode.

public Interface CustomerCRUD
{
    public void Create(Customer customer);
    public Customer Read(int CustomerID);
    public void Update(Customer customer);
    public void Delete(int CustomerID);
}

Bob Martin mengatakan bahwa "kelas seharusnya hanya memiliki satu alasan untuk berubah." Tapi itu sulit untuk membungkus pikiran Anda jika Anda seorang programmer yang baru mengenal SOLID.

Saya menulis jawaban untuk pertanyaan lain , di mana saya menyarankan bahwa tanggung jawab itu seperti jabatan, dan menari di sekitar subjek dengan menggunakan metafora restoran untuk mengilustrasikan poin saya. Tetapi itu masih belum mengartikulasikan serangkaian prinsip yang dapat digunakan seseorang untuk mendefinisikan tanggung jawab kelas mereka.

Jadi, bagaimana Anda melakukannya? Bagaimana Anda menentukan tanggung jawab mana yang harus dimiliki setiap kelas, dan bagaimana Anda mendefinisikan tanggung jawab dalam konteks SRP?

Robert Harvey
sumber
28
Posting ke Review Kode dan ripped terpisah :-D
Jörg W Mittag
8
@ JörgWMittag Hai sekarang, jangan menakut-nakuti orang :)
Flambino
118
Pertanyaan-pertanyaan seperti ini dari anggota veteran menunjukkan bahwa aturan dan prinsip yang kami usahakan tidak berarti langsung atau sederhana . Mereka [semacam] kontradiktif dan mistis ... sebagaimana seharusnya seperangkat aturan yang baik. Dan, saya ingin mempercayai pertanyaan seperti ini merendahkan orang bijak, dan memberi harapan kepada mereka yang merasa sangat bodoh. Terima kasih, Robert!
svidgen
41
Saya ingin tahu apakah pertanyaan ini akan langsung diturunkan + ditandai duplikat langsung jika diposkan oleh noob :)
Andrejs
9
@rmunn: atau dengan kata lain - rep besar menarik lebih banyak rep, karena tidak ada yang membatalkan prasangka dasar manusia pada stackexchange
Andrejs

Jawaban:

117

Salah satu cara untuk memahami hal ini adalah dengan membayangkan kemungkinan perubahan persyaratan dalam proyek masa depan dan tanyakan pada diri sendiri apa yang perlu Anda lakukan untuk mewujudkannya.

Sebagai contoh:

Persyaratan bisnis baru: Pengguna yang berlokasi di California mendapatkan diskon khusus.

Contoh perubahan "baik": Saya perlu memodifikasi kode di kelas yang menghitung diskon.

Contoh perubahan buruk: Saya perlu memodifikasi kode di kelas Pengguna, dan perubahan itu akan memiliki efek berjenjang pada kelas lain yang menggunakan kelas Pengguna, termasuk kelas yang tidak ada hubungannya dengan diskon, misalnya pendaftaran, enumerasi, dan manajemen.

Atau:

Persyaratan nonfungsional baru: Kami akan mulai menggunakan Oracle alih-alih SQL Server

Contoh perubahan yang baik: Hanya perlu memodifikasi satu kelas di lapisan akses data yang menentukan bagaimana mempertahankan data dalam DTO.

Perubahan buruk: Saya perlu memodifikasi semua kelas lapisan bisnis saya karena mengandung logika khusus SQL Server.

Idenya adalah untuk meminimalkan jejak perubahan potensial di masa depan, membatasi modifikasi kode ke satu area kode per area perubahan.

Paling tidak, kelas Anda harus memisahkan masalah logis dari masalah fisik. Satu set besar contoh dapat ditemukan dalam System.IOnamespace: sana kita dapat menemukan berbagai macam aliran fisik (misalnya FileStream, MemoryStreamatau NetworkStream) dan berbagai pembaca dan penulis ( BinaryWriter, TextWriter) yang bekerja pada tingkat logis. Dengan memisahkan mereka dengan cara ini, kita menghindari ledakan combinatoric: bukan perlu FileStreamTextWriter, FileStreamBinaryWriter, NetworkStreamTextWriter, NetworkStreamBinaryWriter, MemoryStreamTextWriter, dan MemoryStreamBinaryWriter, Anda hanya menghubungkan penulis dan sungai dan Anda dapat memiliki apa yang Anda inginkan. Kemudian nanti kita dapat menambahkan, katakanlah, dan XmlWriter, tanpa perlu mengimplementasikannya kembali untuk memori, file, dan jaringan secara terpisah.

John Wu
sumber
34
Sementara saya setuju dengan pemikiran ke depan, ada prinsip-prinsip seperti YAGNI dan metodologi seperti TDD yang menyarankan sebaliknya.
Robert Harvey
87
YAGNI memberitahu kita untuk tidak membangun barang-barang yang tidak kita butuhkan hari ini. Itu tidak mengatakan untuk tidak membangun barang dengan cara yang dapat diperpanjang. Lihat juga prinsip terbuka / tertutup , yang menyatakan "entitas perangkat lunak (kelas, modul, fungsi, dll.) Harus terbuka untuk ekstensi, tetapi ditutup untuk modifikasi."
John Wu
18
@JohnW: +1 untuk komentar YAGNI Anda sendiri. Saya tidak dapat percaya betapa saya harus menjelaskan kepada orang-orang bahwa YAGNI bukan alasan untuk membangun sistem kaku dan tidak fleksibel yang tidak dapat bereaksi terhadap perubahan - ironisnya sangat berlawanan dengan tujuan SRP dan kepala sekolah Terbuka / Tertutup.
Greg Burghardt
36
@ JohnWu: Saya tidak setuju, YAGNI memberi tahu kami tepatnya untuk tidak membangun barang yang tidak kami butuhkan hari ini. Keterbacaan dan tes, misalnya, adalah sesuatu yang selalu dibutuhkan oleh sebuah program "hari ini", jadi YAGNI tidak pernah menjadi alasan untuk tidak menambahkan struktur dan titik injeksi. Namun, segera setelah "ekstensibilitas" menambah biaya signifikan yang manfaatnya tidak jelas "hari ini", YAGNI berarti menghindari jenis ekstensibilitas ini, karena yang terakhir mengarah pada rekayasa yang berlebihan.
Doc Brown
9
@ JohnWu Kami memang beralih dari SQL 2008 ke 2012. Ada total dua pertanyaan yang perlu diubah. Dan dari SQL Auth ke tepercaya? Mengapa itu bahkan menjadi perubahan kode; mengubah koneksiString dalam file konfigurasi sudah cukup. Sekali lagi, YAGNI. YAGNI dan SRP kadang-kadang saling bersaing, dan Anda perlu menilai mana yang memiliki biaya / manfaat yang lebih baik.
Andy
76

Secara praktis, tanggung jawab dibatasi oleh hal-hal yang cenderung berubah. Jadi, sayangnya, tidak ada cara ilmiah atau formula untuk sampai pada apa yang merupakan tanggung jawab. Itu panggilan penilaian.

Ini tentang apa, dalam pengalaman Anda , kemungkinan akan berubah.

Kita cenderung menerapkan bahasa prinsip dalam kemarahan hiperbolik, harfiah, bersemangat. Kita cenderung memecah kelas karena bisa berubah, atau sejalan yang membantu kita memecahkan masalah. (Alasan terakhir ini pada dasarnya tidak buruk.) Tetapi, SRP tidak ada untuk kepentingannya sendiri; itu dalam layanan untuk membuat perangkat lunak yang dapat dipelihara.

Jadi sekali lagi, jika divisi tidak didorong oleh kemungkinan perubahan, mereka tidak benar - benar melayani SRP 1 jika YAGNI lebih berlaku. Keduanya melayani tujuan akhir yang sama. Dan keduanya adalah masalah penilaian - semoga penilaian yang berpengalaman .

Ketika Paman Bob menulis tentang ini, ia menyarankan agar kita memikirkan "tanggung jawab" dalam hal "siapa yang meminta perubahan." Dengan kata lain, kita tidak ingin Partai A kehilangan pekerjaan mereka karena Partai B meminta perubahan.

Ketika Anda menulis modul perangkat lunak, Anda ingin memastikan bahwa ketika perubahan diminta, perubahan itu hanya dapat berasal dari satu orang, atau lebih tepatnya, satu kelompok orang yang sangat erat yang mewakili satu fungsi bisnis yang didefinisikan secara sempit. Anda ingin mengisolasi modul Anda dari kompleksitas organisasi secara keseluruhan, dan merancang sistem Anda sedemikian rupa sehingga setiap modul bertanggung jawab (merespons) kebutuhan hanya dari satu fungsi bisnis tersebut. ( Paman Bob - Prinsip Tanggung Jawab Tunggal )

Pengembang yang baik dan berpengalaman akan merasakan perubahan yang mungkin terjadi. Dan daftar mental itu akan sedikit berbeda menurut industri dan organisasi.

Apa yang merupakan tanggung jawab dalam aplikasi khusus Anda, di organisasi khusus Anda, pada akhirnya adalah masalah penilaian yang berpengalaman . Ini tentang apa yang mungkin berubah. Dan, dalam arti tertentu, ini tentang siapa yang memiliki logika internal modul.


1. Agar lebih jelas, itu tidak berarti mereka divisi yang buruk . Mereka bisa menjadi divisi hebat yang meningkatkan pembacaan kode secara dramatis. Itu hanya berarti mereka tidak didorong oleh SRP.

svidgen
sumber
11
Jawaban terbaik, dan sebenarnya mengutip pikiran Paman Bob. Adapun apa yang mungkin berubah, semua orang mempermasalahkan I / O, "bagaimana jika kita mengubah database?" atau "bagaimana jika kita beralih dari XML ke JSON?" Saya pikir ini biasanya salah arah. Pertanyaan sebenarnya adalah "bagaimana jika kita perlu mengubah int ini menjadi float, menambahkan bidang, dan mengubah String ini ke Daftar String?"
user949300
2
Ini curang. Tanggung jawab tunggal dengan sendirinya hanyalah cara yang diusulkan untuk "mengubah isolasi". Menjelaskan, bahwa Anda perlu mengisolasi perubahan untuk menjaga tanggung jawab "tunggal", tidak menyarankan cara melakukan ini, hanya menjelaskan asal persyaratan.
Basilevs
6
@ Basilevs saya mencoba mengasah kekurangan yang Anda lihat dalam jawaban ini - belum lagi jawaban Paman Bob! Tapi, mungkin saya perlu mengklarifikasi bahwa SRP bukan tentang memastikan bahwa "perubahan" hanya akan berdampak pada 1 kelas. Ini tentang memastikan bahwa setiap kelas akan merespons hanya "satu perubahan." ... Ini tentang mencoba menggambar panah Anda dari setiap kelas ke satu pemilik. Tidak dari masing-masing pemilik ke satu kelas.
svidgen
2
Terima kasih telah memberikan respons pragmatis! Bahkan Paman Bob memperingatkan terhadap ketaatan pada prinsip-prinsip SOLID dalam Arsitektur Agile . Saya tidak memiliki kutipan yang berguna, tetapi dia pada dasarnya mengatakan bahwa membagi tanggung jawab secara inheren meningkatkan tingkat abstraksi dalam kode Anda dan bahwa semua abstraksi dikenakan biaya, jadi pastikan manfaat mengikuti SRP (atau prinsip lain) melebihi biaya. menambahkan lebih banyak abstraksi. (lanjutan komentar berikutnya)
Michael L.
4
Inilah sebabnya mengapa kita harus menempatkan produk di depan pelanggan sedini dan sesering mungkin, sehingga mereka akan memaksa perubahan dalam desain kita dan kita dapat melihat area apa yang cenderung berubah dalam produk itu. Selain itu, ia memperingatkan bahwa kita tidak dapat melindungi diri dari setiap jenis perubahan. Untuk aplikasi apa pun , jenis perubahan tertentu akan sulit dilakukan. Kita perlu memastikan itu adalah perubahan yang paling tidak mungkin terjadi.
Michael L.
29

Saya mengikuti "kelas harus hanya memiliki satu alasan untuk berubah".

Bagi saya, ini berarti memikirkan skema yang keliru yang mungkin muncul dari pemilik produk saya ("Kita perlu mendukung seluler!", "Kita harus pergi ke cloud!", "Kita perlu mendukung China!"). Desain yang baik akan membatasi dampak skema ini ke area yang lebih kecil, dan membuatnya relatif mudah untuk dicapai. Desain yang buruk berarti pergi ke banyak kode dan membuat banyak perubahan yang berisiko.

Pengalaman adalah satu-satunya hal yang saya temukan untuk mengevaluasi dengan benar kemungkinan skema-skema gila itu - karena membuat yang mudah mungkin membuat dua yang lain lebih sulit - dan mengevaluasi kebaikan suatu desain. Pemrogram berpengalaman dapat membayangkan apa yang harus mereka lakukan untuk mengubah kode, apa yang ada di sekitar untuk menggigit mereka, dan trik apa yang membuat segalanya mudah. Pemrogram berpengalaman memiliki firasat yang baik untuk bagaimana kacau mereka ketika pemilik produk meminta hal-hal gila.

Secara praktis, saya menemukan bahwa tes unit membantu di sini. Jika kode Anda tidak fleksibel, akan sulit untuk menguji. Jika Anda tidak dapat menyuntikkan tiruan atau data pengujian lainnya, Anda mungkin tidak akan bisa menyuntikkan SupportChinesekode itu.

Metrik kasar lainnya adalah pitch elevator. Pitch elevator tradisional adalah "jika Anda berada dalam lift dengan seorang investor, dapatkah Anda menjualnya pada sebuah ide?". Startup harus memiliki deskripsi singkat dan sederhana tentang apa yang mereka lakukan - apa fokus mereka. Demikian juga, kelas (dan fungsi) harus memiliki deskripsi sederhana tentang apa yang mereka lakukan . Bukan "kelas ini mengimplementasikan beberapa fubar sehingga Anda dapat menggunakannya dalam skenario khusus ini". Sesuatu yang dapat Anda beri tahu pengembang lain: "Kelas ini menciptakan pengguna". Jika Anda tidak dapat mengomunikasikannya kepada pengembang lain, Anda akan mendapatkan bug.

Telastyn
sumber
Kadang-kadang Anda menerapkan apa yang Anda pikir akan menjadi perubahan yang berantakan, dan ternyata sederhana, atau refactor kecil membuatnya sederhana, dan menambahkan fungsionalitas yang bermanfaat pada saat yang sama. Tapi ya, biasanya Anda bisa melihat kesulitan datang.
16
Saya seorang penganjur besar untuk ide "elevator pitch". Jika sulit untuk menjelaskan apa yang dilakukan kelas dalam satu atau dua kalimat, Anda berada di wilayah berisiko.
Ivan
1
Anda menyentuh satu poin penting: kemungkinan skema gila itu bervariasi secara dramatis dari satu pemilik proyek ke pemilik berikutnya. Anda harus mengandalkan tidak hanya pada pengalaman umum Anda, tetapi pada seberapa baik Anda mengenal pemilik proyek. Saya telah bekerja untuk orang-orang yang ingin mengurangi sprint kami menjadi satu minggu, dan masih tidak bisa menghindari perubahan arah sprint tengah.
Kevin Krumwiede
1
Selain manfaat yang jelas, mendokumentasikan kode Anda menggunakan "elevator pitches" juga berfungsi untuk membantu Anda memikirkan apa yang dilakukan kode Anda menggunakan bahasa alami yang saya temukan berguna dalam mengungkap banyak tanggung jawab.
Alexander
1
@KevinKrumwiede Untuk itulah metodologi "Ayam berlarian dengan kepalanya terpotong" dan "Wild Goose Chase"!
26

Tidak ada yang tahu. Atau setidaknya, kami tidak dapat menyetujui satu definisi. Itulah yang membuat SPR (dan prinsip-prinsip SOLID lainnya) cukup kontroversial.

Saya berpendapat bisa mengetahui apa yang menjadi tanggung jawab atau tidak adalah salah satu keterampilan yang harus dipelajari oleh pengembang perangkat lunak selama kariernya. Semakin banyak kode yang Anda tulis dan tinjau, semakin banyak pengalaman yang harus Anda tentukan jika sesuatu itu tanggung jawab tunggal atau ganda. Atau jika tanggung jawab tunggal terpecah di bagian kode yang terpisah.

Saya berpendapat bahwa tujuan utama dari SRP bukanlah untuk menjadi aturan yang sulit. Hal ini untuk mengingatkan kita agar memperhatikan kohesi dalam kode dan untuk selalu berusaha secara sadar menentukan kode mana yang kohesif dan mana yang tidak.

Euforia
sumber
20
Pemrogram baru memang memiliki kecenderungan untuk memperlakukan SOLID seolah-olah itu adalah serangkaian hukum, padahal sebenarnya tidak. Ini hanya pengelompokan ide-ide bagus untuk membantu orang menjadi lebih baik di desain kelas. Sayangnya, orang cenderung menganggap prinsip-prinsip ini terlalu serius; Baru-baru ini saya melihat posting pekerjaan yang menyebut SOLID sebagai salah satu persyaratan pekerjaan.
Robert Harvey
9
+42 untuk paragraf terakhir. Seperti yang dikatakan @RobertHarvey, hal-hal seperti SPR, SOLID dan YAGNI tidak boleh dianggap sebagai " aturan absolut ", tetapi sebagai prinsip umum "nasihat yang baik". Di antara mereka (dan yang lain), saran itu kadang-kadang akan saling bertentangan, tetapi menyeimbangkan saran itu (sebagai lawan mengikuti serangkaian aturan yang kaku) akan (seiring waktu, seiring dengan meningkatnya pengalaman Anda) membimbing Anda untuk menghasilkan perangkat lunak yang lebih baik. Seharusnya hanya ada satu "aturan absolut" dalam pengembangan perangkat lunak: " Tidak ada aturan absolut ".
TripeHound
Ini klarifikasi yang sangat baik pada satu aspek SRP. Tetapi, bahkan jika prinsip-prinsip SOLID bukanlah aturan yang keras, mereka tidak terlalu berharga jika tidak ada yang mengerti apa artinya - apalagi jika klaim Anda bahwa "tidak ada yang tahu" sebenarnya benar! ... masuk akal bagi mereka untuk sulit dimengerti. Seperti halnya keterampilan, ada sesuatu yang membedakan yang baik dari yang tidak baik! Tapi ... "tidak ada yang tahu" menjadikannya lebih sebagai ritual perpeloncoan. (Dan saya tidak percaya itu maksud dari SOLID!)
svidgen
3
Dengan "Tidak ada yang tahu", saya harap @Euphoric berarti bahwa tidak ada definisi yang tepat yang akan bekerja untuk setiap kasus penggunaan. Itu adalah sesuatu yang membutuhkan tingkat penilaian. Saya pikir salah satu cara terbaik untuk menentukan di mana tanggung jawab Anda terletak adalah untuk beralih dengan cepat dan biarkan basis kode Anda memberi tahu Anda . Cari "bau" yang kode Anda tidak mudah dipelihara. Misalnya, ketika perubahan pada aturan bisnis tunggal mulai memiliki dampak berjenjang melalui kelas yang tampaknya tidak terkait, Anda mungkin memiliki pelanggaran SRP.
Michael L.
1
Saya sungguh-sungguh kedua @ TripeHound dan lainnya yang telah menunjukkan bahwa semua "aturan" ini tidak ada untuk mendefinisikan One True Religion of development, tetapi untuk meningkatkan kemungkinan mengembangkan perangkat lunak yang dapat dirawat. Berhati-hatilah dalam mengikuti "praktik terbaik" jika Anda tidak dapat menjelaskan bagaimana mempromosikan perangkat lunak yang dapat dipelihara, meningkatkan kualitas, atau meningkatkan efisiensi pengembangan ..
Michael L.
5

Saya pikir istilah "tanggung jawab" berguna sebagai metafora karena memungkinkan kita untuk menggunakan perangkat lunak untuk menyelidiki seberapa baik perangkat lunak tersebut dikelola. Secara khusus, saya akan fokus pada dua prinsip:

  • Tanggung jawab sepadan dengan otoritas.
  • Tidak ada dua entitas yang bertanggung jawab untuk hal yang sama.

Kedua prinsip ini marilah kita membagikan tanggung jawab secara bermakna karena mereka saling mempermainkan. Jika Anda memberdayakan sepotong kode untuk melakukan sesuatu untuk Anda, itu harus memiliki tanggung jawab atas apa yang dilakukannya. Ini menyebabkan tanggung jawab bahwa kelas mungkin harus tumbuh, memperluas itu "satu alasan untuk berubah" ke lingkup yang lebih luas dan lebih luas. Namun, ketika Anda membuat sesuatu lebih luas, Anda secara alami mulai mengalami situasi di mana banyak entitas bertanggung jawab untuk hal yang sama. Ini penuh dengan masalah dalam tanggung jawab kehidupan nyata, jadi pasti itu adalah masalah dalam pengkodean juga. Akibatnya, prinsip ini menyebabkan lingkup menjadi sempit, saat Anda membagi tanggung jawab ke dalam paket yang tidak terduplikasi.

Selain kedua hal ini, prinsip ketiga tampaknya masuk akal:

  • Tanggung jawab dapat didelegasikan

Pertimbangkan program yang baru saja dicetak ... batu tulis kosong. Pada awalnya, Anda hanya memiliki satu entitas, yang merupakan program secara keseluruhan. Itu bertanggung jawab untuk ... semuanya. Secara alami pada titik tertentu Anda akan mulai mendelegasikan tanggung jawab ke fungsi atau kelas. Pada titik ini, dua aturan pertama berperan memaksa Anda untuk menyeimbangkan tanggung jawab itu. Program tingkat atas masih bertanggung jawab atas output keseluruhan, seperti halnya seorang manajer bertanggung jawab atas produktivitas tim mereka, tetapi setiap sub-entitas telah didelegasikan tanggung jawab, dan dengan itu wewenang untuk melaksanakan tanggung jawab itu.

Sebagai bonus tambahan, ini membuat SOLID sangat kompatibel dengan pengembangan perangkat lunak perusahaan yang mungkin perlu dilakukan. Setiap perusahaan di planet ini memiliki konsep bagaimana mendelegasikan tanggung jawab, dan mereka tidak semua setuju. Jika Anda mendelegasikan tanggung jawab dalam perangkat lunak Anda dengan cara yang mengingatkan pada delegasi perusahaan Anda sendiri, akan jauh lebih mudah bagi pengembang masa depan untuk mempercepat dengan bagaimana Anda melakukan sesuatu di perusahaan ini.

Cort Ammon
sumber
Saya tidak 100% yakin ini sepenuhnya menjelaskannya. Tapi, saya pikir menjelaskan "tanggung jawab" sehubungan dengan "otoritas" adalah cara yang mendalam untuk mengungkapkannya! (+1)
svidgen
Pirsig berkata, "Anda cenderung membuat masalah Anda di mesin", yang membuat saya berhenti sejenak.
@nocomprende Anda juga cenderung membangun kekuatan Anda ke dalam mesin. Saya berpendapat bahwa ketika kekuatan dan kelemahan Anda adalah hal yang sama, saat itulah ia menjadi menarik.
Cort Ammon
5

Dalam konferensi di Yale ini, Paman Bob memberikan contoh lucu ini :

Masukkan deskripsi gambar di sini

Dia mengatakan bahwa Employeeada tiga alasan untuk berubah, tiga sumber persyaratan perubahan, dan memberikan penjelasan yang lucu dan tidak jelas ini , tetapi tetap ilustratif:

  • Jika CalcPay()metode ini memiliki kesalahan dan biaya perusahaan jutaan US $, CFO akan memecat Anda .

  • Jika ReportHours()metode ini memiliki kesalahan dan biaya perusahaan jutaan US $, COO akan memecat Anda .

  • Jika WriteEmmployee(metode) memiliki kesalahan yang menyebabkan penghapusan banyak data dan menelan biaya jutaan dolar bagi perusahaan, CTO akan memecat Anda .

Jadi memiliki tiga eksekutif level C yang berbeda berpotensi memecat Anda karena kesalahan yang mahal di kelas yang sama berarti kelas tersebut memiliki terlalu banyak tanggung jawab.

Dia memberikan solusi ini yang memecahkan pelanggaran SRP, tetapi belum menyelesaikan pelanggaran DIP yang tidak ditampilkan dalam video.

Masukkan deskripsi gambar di sini

Tulains Córdova
sumber
Contoh ini lebih mirip kelas yang memiliki tanggung jawab yang salah .
Robert Harvey
4
@RobertHarvey Ketika sebuah kelas memiliki terlalu banyak tanggung jawab, itu berarti bahwa tanggung jawab ekstra adalah tanggung jawab yang salah .
Tulains Córdova
5
Saya mendengar apa yang Anda katakan, tetapi saya tidak menganggapnya menarik. Ada perbedaan antara kelas yang memiliki terlalu banyak tanggung jawab dan kelas yang melakukan sesuatu yang tidak ada urusannya sama sekali. Mungkin terdengar sama, tetapi tidak; menghitung kacang tidak sama dengan memanggil mereka kacang kenari. Itu adalah prinsip Paman Bob dan contoh Paman Bob, tetapi jika itu cukup deskriptif, kita tidak akan memerlukan pertanyaan ini sama sekali.
Robert Harvey
@RobertHarvey, apa bedanya? Situasi itu sepertinya isomorfik bagi saya.
Paul Draper
3

Saya pikir cara yang lebih baik untuk membagi hal-hal daripada "alasan untuk berubah" adalah mulai dengan berpikir dalam hal apakah akan masuk akal untuk meminta kode yang perlu melakukan melakukan dua (atau lebih) tindakan harus perlu memegang referensi objek yang terpisah untuk setiap tindakan, dan apakah akan bermanfaat untuk memiliki objek publik yang dapat melakukan satu tindakan tetapi tidak yang lain.

Jika jawaban untuk kedua pertanyaan adalah ya, itu akan menyarankan tindakan harus dilakukan oleh kelas yang berbeda. Jika jawaban untuk kedua pertanyaan tidak, itu akan menyarankan bahwa dari sudut pandang publik harus ada satu kelas; jika kode untuk itu akan sulit digunakan, mungkin secara internal dibagi menjadi kelas-kelas pribadi. Jika jawaban untuk pertanyaan pertama adalah tidak, tetapi yang kedua adalah ya, harus ada kelas terpisah untuk setiap tindakan ditambah kelas komposit yang mencakup referensi untuk contoh dari yang lain.

Jika seseorang memiliki kelas terpisah untuk keypad, bunyi bip, pembacaan numerik, printer penerimaan, dan laci kasir, dan tidak ada kelas komposit untuk mesin kasir yang lengkap, maka kode yang seharusnya memproses transaksi mungkin berakhir tanpa sengaja dipanggil dalam cara yang mengambil input dari keypad satu mesin, menghasilkan suara dari bip mesin kedua, menunjukkan angka pada tampilan mesin ketiga, mencetak tanda terima pada printer mesin keempat, dan mengeluarkan laci uang mesin kelima. Masing-masing sub fungsi tersebut mungkin berguna ditangani oleh kelas yang terpisah, tetapi harus ada kelas komposit yang bergabung dengan mereka. Kelas komposit harus mendelegasikan logika sebanyak mungkin ke kelas konstituen,

Orang bisa mengatakan bahwa "tanggung jawab" masing-masing kelas adalah menggabungkan beberapa logika nyata atau memberikan titik lampiran umum untuk beberapa kelas lain yang melakukannya, tetapi yang penting adalah fokus terlebih dahulu dan terutama tentang bagaimana kode klien harus melihat kelas. Jika masuk akal untuk kode klien untuk melihat sesuatu sebagai objek tunggal, maka kode klien harus melihatnya sebagai objek tunggal.

supercat
sumber
Ini saran yang bagus. Mungkin perlu menunjukkan bahwa Anda membagi tanggung jawab berdasarkan kriteria lebih dari hanya SRP.
Jørgen Fogh
1
Analogi mobil: Saya tidak perlu tahu berapa banyak gas yang ada di tangki orang lain, atau ingin menghidupkan wiper orang lain. (tapi itu definisi internet) (Sst! Anda akan merusak ceritanya)
1
@nocomprende - "Saya tidak perlu tahu berapa banyak bensin di tangki orang lain," - kecuali Anda seorang remaja yang mencoba memutuskan mobil keluarga mana yang akan "dipinjam" untuk perjalanan Anda berikutnya ...;)
alephzero
3

SRP sulit untuk diperbaiki. Sebagian besar masalah menugaskan 'pekerjaan' ke kode Anda dan memastikan setiap bagian memiliki tanggung jawab yang jelas. Seperti dalam kehidupan nyata, dalam beberapa kasus membagi pekerjaan di antara orang-orang bisa sangat alami, tetapi dalam kasus lain mungkin benar-benar rumit, terutama jika Anda tidak mengenal mereka (atau pekerjaan).

Saya selalu menyarankan Anda hanya menulis kode sederhana yang berfungsi pertama , kemudian sedikit refactor: Anda akan cenderung melihat bagaimana kode cluster secara alami setelah beberapa saat. Saya pikir itu adalah kesalahan untuk memaksakan tanggung jawab sebelum Anda tahu kode (atau orang) dan pekerjaan yang harus dilakukan.

Satu hal yang akan Anda perhatikan adalah ketika modul mulai melakukan terlalu banyak dan sulit untuk debug / pemeliharaan. Inilah saatnya untuk refactor; apa yang seharusnya menjadi pekerjaan inti dan tugas apa yang bisa diberikan ke modul lain? Misalnya, haruskah ia menangani pemeriksaan keamanan dan pekerjaan lainnya, atau haruskah Anda melakukan pemeriksaan keamanan di tempat lain terlebih dahulu, atau akankah ini membuat kode lebih rumit?

Gunakan terlalu banyak tipuan dan menjadi berantakan lagi ... karena untuk prinsip-prinsip lain, ini akan bertentangan dengan yang lain, seperti KISS, YAGNI, dll. Semuanya adalah masalah keseimbangan.

Christophe Roussy
sumber
Bukankah SRP hanyalah Kekompakan Konstantinus yang besar?
Nick Keighley
Anda secara alami akan menemukan pola-pola itu jika Anda kode cukup lama, tetapi Anda dapat mempercepat pembelajaran dengan memberi nama mereka dan itu membantu dengan komunikasi ...
Christophe Roussy
@NickKeighley Saya pikir itu kohesi, tidak begitu banyak tulisan besar, tetapi melihat dari perspektif lain.
sdenham
3

"Prinsip tanggung jawab tunggal" mungkin nama yang membingungkan. "Hanya satu alasan untuk berubah" adalah deskripsi prinsip yang lebih baik, tetapi masih mudah disalahpahami. Kami tidak berbicara tentang mengatakan apa yang menyebabkan objek berubah status saat runtime. Kami sedang mempertimbangkan apa yang dapat menyebabkan pengembang harus mengubah kode di masa mendatang.

Kecuali jika kami memperbaiki bug, perubahan akan terjadi karena persyaratan bisnis baru atau yang diubah. Anda harus berpikir di luar kode itu sendiri, dan bayangkan faktor luar apa yang menyebabkan persyaratan berubah secara independen . Mengatakan:

  • Tarif pajak berubah karena keputusan politik.
  • Pemasaran memutuskan untuk mengubah nama semua produk
  • UI harus dirancang ulang agar dapat diakses
  • Basis data padat, jadi Anda perlu melakukan beberapa optimasi
  • Anda harus mengakomodasi aplikasi seluler
  • dan seterusnya...

Idealnya Anda ingin faktor independen mempengaruhi kelas yang berbeda. Misalnya karena tarif pajak berubah independen dari nama produk, perubahan tidak boleh mempengaruhi kelas yang sama. Kalau tidak, Anda menjalankan risiko pengenaan perubahan pajak kesalahan dalam penamaan produk, yang merupakan jenis kopling ketat yang ingin Anda hindari dengan sistem modular.

Jadi jangan hanya fokus pada apa yang bisa berubah - apa pun mungkin berubah di masa depan. Fokus pada apa yang mungkin berubah secara independen . Perubahan biasanya bersifat independen jika disebabkan oleh aktor yang berbeda.

Contoh Anda dengan judul pekerjaan ada di jalur yang benar, tetapi Anda harus menerimanya secara lebih harfiah! Jika pemasaran dapat menyebabkan perubahan pada kode dan keuangan dapat menyebabkan perubahan lainnya, perubahan ini seharusnya tidak mempengaruhi kode yang sama, karena ini adalah jabatan pekerjaan yang berbeda dan karenanya perubahan akan terjadi secara independen.

Mengutip Paman Bob yang menemukan istilah:

Ketika Anda menulis modul perangkat lunak, Anda ingin memastikan bahwa ketika perubahan diminta, perubahan itu hanya dapat berasal dari satu orang, atau lebih tepatnya, satu kelompok orang yang sangat erat yang mewakili satu fungsi bisnis yang didefinisikan secara sempit. Anda ingin mengisolasi modul Anda dari kompleksitas organisasi secara keseluruhan, dan merancang sistem Anda sedemikian rupa sehingga setiap modul bertanggung jawab (merespons) kebutuhan hanya dari satu fungsi bisnis tersebut.

Singkatnya: "Tanggung jawab" melayani fungsi bisnis tunggal. Jika lebih dari satu aktor dapat menyebabkan Anda harus mengubah kelas, maka kelas tersebut mungkin melanggar prinsip ini.

JacquesB
sumber
Menurut bukunya "Arsitektur Bersih" ini tepat. Aturan bisnis harus berasal dari satu sumber, dan hanya satu sumber saja. Ini berarti bahwa SDM, Operasi dan TI semua perlu bekerja sama merumuskan persyaratan dalam "Tanggung Jawab Tunggal". Dan itu prinsipnya. +1
Benny Skogberg
2

Artikel bagus yang menjelaskan prinsip-prinsip pemrograman SOLID dan memberikan contoh kode baik mengikuti dan tidak mengikuti prinsip-prinsip ini adalah https://scotch.io/bar-talk/solid-the-first-five-principles-of-object-oriented- desain .

Dalam contoh yang berkaitan dengan SRP ia memberikan contoh beberapa kelas bentuk (lingkaran dan bujur sangkar) dan kelas yang dirancang untuk menghitung luas total beberapa bentuk.

Dalam contoh pertamanya, ia menciptakan kelas penghitungan area dan mengembalikan hasilnya sebagai HTML. Kemudian dia memutuskan ingin menampilkannya sebagai JSON dan harus mengubah kelas penghitungan luasnya.

Masalah dengan contoh ini adalah bahwa kelas penghitungan area bertanggung jawab untuk menghitung area bentuk DAN menampilkan area itu. Dia kemudian melalui cara yang lebih baik untuk melakukan ini menggunakan kelas lain yang dirancang khusus untuk menampilkan area.

Ini adalah contoh sederhana (dan lebih mudah dipahami membaca artikel karena memiliki potongan kode) tetapi menunjukkan gagasan inti SRP.

blitz1616
sumber
0

Pertama-tama, apa yang Anda miliki sebenarnya adalah dua masalah yang terpisah : masalah metode apa yang harus dimasukkan ke dalam kelas Anda, dan masalah antarmuka yang menggembung.

Antarmuka

Anda memiliki antarmuka ini:

public Interface CustomerCRUD
{
  public void Create(Customer customer);
  public Customer Read(int CustomerID);
  public void Update(Customer customer);
  public void Delete(int CustomerID);
}

Agaknya, Anda memiliki beberapa kelas yang sesuai dengan CustomerCRUDantarmuka (jika tidak, antarmuka tidak perlu), dan beberapa fungsi do_crud(customer: CustomerCRUD)yang mengambil objek yang sesuai. Tetapi Anda telah melanggar SRP: Anda telah mengikat keempat operasi yang berbeda ini bersama-sama.

Katakanlah nanti Anda akan beroperasi pada tampilan basis data. Pandangan database memiliki hanya satu Readmetode yang tersedia untuk itu. Tetapi Anda ingin menulis fungsi do_query_stuff(customer: ???)yang operator transparan pada tabel atau tampilan penuh; itu hanya menggunakan Readmetode, setelah semua.

Jadi buat antarmuka

CustomerReader antarmuka publik {public Customer Read (customerID: int)}

dan faktor CustomerCrudantarmuka Anda sebagai:

public interface CustomerCRUD extends CustomerReader
{
  public void Create(Customer customer);
  public void Update(Customer customer);
  public void Delete(int CustomerID);
}

Tapi tidak ada akhir yang terlihat. Mungkin ada benda-benda yang bisa kita buat tetapi tidak diperbarui, dll. Lubang kelinci ini terlalu dalam. Satu-satunya cara yang waras untuk mematuhi prinsip tanggung jawab tunggal adalah membuat semua antarmuka Anda mengandung tepat satu metode . Go sebenarnya mengikuti metodologi ini dari apa yang saya lihat, dengan sebagian besar antarmuka berisi fungsi tunggal; jika Anda ingin menentukan antarmuka yang berisi dua fungsi, Anda harus canggung membuat antarmuka baru yang menggabungkan keduanya. Anda segera mendapatkan ledakan antarmuka kombinatorial.

Jalan keluar dari kekacauan ini adalah dengan menggunakan subtyping struktural (diimplementasikan dalam misalnya OCaml) daripada antarmuka (yang merupakan bentuk subtyping nominal). Kami tidak mendefinisikan antarmuka; alih-alih, kita cukup menulis fungsi

let do_customer_stuff customer = customer.read ... customer.update ...

yang memanggil metode apa pun yang kita suka. OCaml akan menggunakan tipe inferensi untuk menentukan bahwa kita dapat meneruskan objek apa pun yang mengimplementasikan metode ini. Dalam contoh ini, akan ditentukan yang customerbertipe <read: int -> unit, update: int -> unit, ...>.

Kelas

Ini memecahkan kekacauan antarmuka ; tetapi kita masih harus mengimplementasikan kelas yang berisi banyak metode. Sebagai contoh, haruskah kita membuat dua kelas yang berbeda, CustomerReaderdan CustomerWriter? Bagaimana jika kita ingin mengubah cara tabel dibaca (mis. Kita sekarang menyimpan respons kita dalam redis sebelum setelah mengambil data), tetapi sekarang bagaimana mereka dituliskan? Jika Anda mengikuti ini rantai penalaran kesimpulan logis, Anda memimpin terkait dengan pemrograman fungsional :)

kepala kebun
sumber
4
"Tidak berarti" agak kuat. Saya bisa berada di belakang "mistis" atau "Zen." Tapi, tidak langsung sia
svidgen
Bisakah Anda menjelaskan sedikit lebih banyak mengapa subtyping struktural adalah solusi?
Robert Harvey
@RobertHarvey Merestrukturisasi jawaban saya secara signifikan
gardenhead
4
Saya menggunakan antarmuka bahkan ketika saya hanya memiliki satu kelas mengimplementasikannya. Mengapa? Mengejek dalam unit-test.
Eternal21
0

Dalam pikiran saya, hal terdekat dengan SRP yang muncul di benak saya adalah aliran penggunaan. Jika Anda tidak memiliki aliran penggunaan yang jelas untuk kelas tertentu, itu mungkin kelas Anda memiliki aroma desain.

Alur penggunaan akan menjadi suksesi panggilan metode tertentu yang akan memberi Anda hasil yang diharapkan (dengan demikian dapat diuji). Anda pada dasarnya mendefinisikan kelas dengan use case yang didapatnya IMHO, itu sebabnya semua metodologi program berfokus pada antarmuka daripada implementasi.

Arthur Havlicek
sumber
0

Ini untuk mencapai perubahan beberapa persyaratan, tidak memerlukan komponen Anda untuk berubah .

Tapi semoga berhasil memahami hal itu pada pandangan pertama, ketika Anda pertama kali mendengar tentang SOLID.


Saya melihat banyak komentar yang mengatakan SRP dan YAGNI dapat bertentangan satu sama lain, tetapi YAGN saya ditegakkan oleh TDD (GOOS, London School) mengajari saya untuk memikirkan dan merancang komponen saya dari perspektif klien. Saya mulai mendesain antarmuka saya dengan apa yang paling tidak diinginkan oleh klien mana pun, yaitu hanya sedikit yang harus dilakukan . Dan latihan itu bisa dilakukan tanpa sepengetahuan TDD.

Saya suka teknik yang dijelaskan oleh Paman Bob (saya tidak ingat dari mana, sayangnya), yang kira-kira seperti:

Tanyakan pada diri sendiri, apa yang dilakukan kelas ini?

Apakah jawaban Anda mengandung salah satu dari Dan atau Atau

Jika demikian, ekstrak bagian dari jawaban itu, itu adalah tanggung jawabnya sendiri

Teknik ini mutlak, dan seperti yang dikatakan @svidgen, SRP adalah panggilan penilaian, tetapi ketika mempelajari sesuatu yang baru, absolut adalah yang terbaik, lebih mudah untuk selalu melakukan sesuatu. Pastikan alasan Anda tidak berpisah adalah; perkiraan berpendidikan, dan bukan karena Anda tidak tahu caranya. Ini seni, dan butuh pengalaman.


Saya pikir banyak jawaban tampaknya membuat argumen untuk decoupling ketika berbicara tentang SRP .

SRP adalah tidak memastikan perubahan tidak menyebarkan bawah grafik ketergantungan.

Secara teoritis, tanpa SRP , Anda tidak akan memiliki ketergantungan ...

Satu perubahan seharusnya tidak menyebabkan perubahan banyak tempat dalam aplikasi, tetapi kami mendapat prinsip lain untuk itu. Namun SRP meningkatkan Prinsip Terbuka Tertutup . Prinsip ini lebih tentang abstraksi, namun abstraksi yang lebih kecil lebih mudah untuk diimplementasikan kembali .

Jadi ketika mengajar SOLID secara keseluruhan, berhati-hatilah untuk mengajarkan bahwa SRP memungkinkan Anda untuk mengubah lebih sedikit kode ketika persyaratan berubah, padahal sebenarnya, memungkinkan Anda untuk menulis lebih sedikit kode baru .

Chris Wohlert
sumber
3
When learning something new, absolutes are the best, it is easier to just always do something.- Dalam pengalaman saya, programmer baru terlalu dogmatis. Absolutisme mengarah pada pengembang yang tidak berpikir dan pemrograman kargo-kultus. Mengatakan "lakukan saja ini" tidak apa-apa, selama Anda memahami bahwa orang yang Anda ajak bicara nanti harus melupakan apa yang telah Anda ajarkan kepada mereka.
Robert Harvey
@RobertHarvey, Sepenuhnya benar itu menciptakan perilaku dogmatis, dan Anda harus melepaskan / mempelajari kembali saat Anda mendapatkan pengalaman. Ini adalah poin saya. Jika seorang programmer baru mencoba untuk membuat panggilan penilaian tanpa cara untuk alasan keputusan mereka sepertinya tidak berguna, karena mereka tidak tahu mengapa itu berhasil, ketika itu berhasil. Dengan membuat orang berlebihan , itu mengajarkan mereka untuk mencari pengecualian daripada membuat tebakan yang tidak memenuhi syarat. Segala sesuatu yang Anda katakan tentang absolutisme adalah benar, karena itu seharusnya hanya menjadi titik awal.
Chris Wohlert
@RobertHarvey, Contoh kehidupan nyata yang cepat : Anda mungkin mengajari anak-anak Anda untuk selalu jujur, tetapi ketika mereka tumbuh dewasa, mereka mungkin akan menyadari beberapa pengecualian di mana orang tidak ingin mendengar pikiran mereka yang paling jujur. Mengharapkan anak berusia 5 tahun untuk membuat keputusan penilaian yang benar tentang bersikap jujur ​​adalah yang terbaik. :)
Chris Wohlert
0

Tidak ada jawaban yang jelas untuk itu. Meskipun pertanyaannya sempit, penjelasannya tidak.

Bagi saya, itu adalah sesuatu seperti Occam's Razor jika Anda mau. Ini adalah ideal di mana saya mencoba mengukur kode saya saat ini. Sulit untuk memakukannya dengan kata-kata yang sederhana dan sederhana. Metafora lain adalah »satu topik« yang abstrak, yaitu sulit dipahami, sebagai »tanggung jawab tunggal«. Deskripsi ketiga akan »berurusan dengan satu tingkat abstraksi«.

Apa artinya itu secara praktis?

Akhir-akhir ini saya menggunakan gaya pengkodean yang sebagian besar terdiri dari dua fase:

Fase I paling baik digambarkan sebagai kekacauan kreatif. Pada fase ini saya menuliskan kode ketika pikiran mengalir - yaitu mentah dan jelek.

Fase II adalah kebalikan total. Itu seperti membersihkan setelah badai. Ini membutuhkan kerja dan disiplin paling banyak. Dan kemudian saya melihat kode dari sudut pandang seorang desainer.

Saya bekerja sebagian besar di Python sekarang, yang memungkinkan saya untuk memikirkan objek dan kelas nanti. Pertama Tahap I - saya menulis hanya fungsi dan menyebarkannya hampir secara acak dalam modul yang berbeda. Pada Fase II , setelah saya mulai bekerja, saya melihat lebih dekat pada modul apa yang berhubungan dengan bagian mana dari solusi. Dan ketika membaca sekilas modul, topik muncul kepada saya. Beberapa fungsi terkait secara tematis. Ini adalah kandidat yang bagus untuk kelas . Dan setelah saya mengubah fungsi menjadi kelas - yang hampir dilakukan dengan lekukan dan menambahkan selfke daftar parameter dengan python;) - Saya menggunakan SRPseperti Occam's Razor untuk menghapus fungsionalitas ke modul dan kelas lainnya.

Contoh saat ini mungkin menulis fungsionalitas ekspor kecil beberapa hari yang lalu.

Ada kebutuhan untuk csv , excel , dan gabungan excel sheet di dalam zip.

Fungsionalitas polos masing-masing dilakukan dalam tiga tampilan (= fungsi). Setiap fungsi menggunakan metode umum untuk menentukan filter dan metode kedua untuk mengambil data. Kemudian di setiap fungsi persiapan ekspor berlangsung dan dikirim sebagai Respons dari server.

Ada terlalu banyak level abstraksi yang tercampur:

I) berurusan dengan permintaan / respons masuk / keluar

II) menentukan filter

III) mengambil data

IV) transformasi data

Langkah mudah adalah dengan menggunakan satu abstraksi ( exporter) untuk menangani lapisan II-IV pada langkah pertama.

Yang tersisa hanyalah topik yang berhubungan dengan permintaan / tanggapan . Pada tingkat abstraksi yang sama mengekstraksi parameter permintaan yang tidak apa-apa. Jadi saya punya pandangan ini satu "tanggung jawab".

Kedua, saya harus memecah eksportir, yang seperti yang kita lihat terdiri dari setidaknya tiga lapisan abstraksi.

Menentukan kriteria filter dan retrival sebenarnya hampir pada tingkat abstraksi yang sama (filter diperlukan untuk mendapatkan subset data yang tepat). Level-level ini dimasukkan ke dalam sesuatu seperti lapisan akses data .

Pada langkah berikutnya saya memecah mekanisme ekspor yang sebenarnya terpisah: Di mana menulis ke file temporal diperlukan, saya membagi itu menjadi dua "tanggung jawab": satu untuk penulisan data aktual ke disk dan bagian lain yang berhubungan dengan format aktual.

Seiring pembentukan kelas dan modul, segalanya menjadi lebih jelas, apa yang menjadi milik di mana. Dan selalu pertanyaan laten, apakah kelasnya melakukan terlalu banyak .

Bagaimana Anda menentukan tanggung jawab mana yang harus dimiliki setiap kelas, dan bagaimana Anda mendefinisikan tanggung jawab dalam konteks SRP?

Sulit untuk memberikan resep untuk diikuti. Tentu saja saya bisa mengulangi cryptic »satu level abstraksi« - aturan jika itu membantu.

Bagi saya ini adalah "intuisi artistik" yang mengarah pada desain saat ini; Saya memodelkan kode seperti seniman yang bisa memahat tanah liat atau melukis.

Bayangkan saya sebagai Coding Bob Ross ;)

Thomas Junk
sumber
0

Apa yang saya coba lakukan untuk menulis kode yang mengikuti SRP:

  • Pilih masalah tertentu yang perlu Anda pecahkan;
  • Tulis kode yang menyelesaikannya, tulis semuanya dalam satu metode (mis: main);
  • Analisis kode dengan hati-hati dan, berdasarkan pada bisnis, cobalah untuk mendefinisikan tanggung jawab yang terlihat dalam semua operasi yang sedang dilakukan (ini adalah bagian subjektif yang juga tergantung pada bisnis / proyek / pelanggan);
  • Harap perhatikan bahwa semua fungsi sudah diterapkan; Selanjutnya adalah hanya organisasi kode (tidak ada fitur atau mekanisme tambahan yang akan diterapkan mulai sekarang dalam pendekatan ini);
  • Berdasarkan tanggung jawab yang Anda tetapkan dalam langkah sebelumnya (yang ditentukan berdasarkan bisnis dan ide "satu alasan untuk berubah"), ekstrak kelas atau metode yang terpisah untuk masing-masing;
  • Harap dicatat bahwa pendekatan ini hanya peduli pada SPR; idealnya harus ada langkah-langkah tambahan di sini mencoba mematuhi prinsip-prinsip lain juga.

Contoh:

Masalah: dapatkan dua angka dari pengguna, hitung jumlah mereka dan hasilkan hasilnya kepada pengguna:

//first step: solve the problem right away
static void Main(string[] args)
{
    Console.WriteLine("Number 1: ");
    int firstNumber = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Number 2: ");
    int secondNumber = Convert.ToInt32(Console.ReadLine());

    int result = firstNumber + secondNumber;

    Console.WriteLine("Hi there! The result is: {0}", result);

    Console.ReadLine();
}

Selanjutnya, cobalah untuk mendefinisikan tanggung jawab berdasarkan tugas yang perlu dilakukan. Dari ini, ekstrak kelas yang sesuai:

//Responsible for getting two integers from the user
class Input {
    public int FirstNumber { get; set; }
    public int SecondNumber { get; set; }
    public void Read() {
        Console.WriteLine("Number 1: ");
        FirstNumber = Convert.ToInt32(Console.ReadLine());

        Console.WriteLine("Number 2: ");
        SecondNumber = Convert.ToInt32(Console.ReadLine());
    }
}

//Responsible for calculating the sum of two integers
class SumOperation {
    public int Result { get; set; }
    public void Calculate(int a, int b) {
        Result = a + b;
    }
}

//Responsible for the output of some value to the user
class Output {
    public void Write(int result) {
        Console.WriteLine("Hello! The result is: {0}", result);
    }
}

Kemudian, program refactored menjadi:

//Program: responsible for main execution.
//Gets two numbers from user and output their sum.
static void Main(string[] args)
{
    var input = new Input();
    input.Read();

    var operation = new SumOperation();
    operation.Calculate(input.FirstNumber, input.SecondNumber);

    var output = new Output();
    output.Write(operation.Result);

    Console.ReadLine();
}

Catatan: contoh yang sangat sederhana ini hanya mempertimbangkan prinsip SRP. Penggunaan prinsip-prinsip lain (misalnya: "L" - kode harus bergantung pada abstraksi daripada konkret) akan memberikan lebih banyak manfaat pada kode dan membuatnya lebih dapat dipertahankan untuk perubahan bisnis.

Emerson Cardoso
sumber
1
Contoh Anda terlalu sederhana untuk menggambarkan SRP secara memadai. Tidak ada yang akan melakukan ini di kehidupan nyata.
Robert Harvey
Ya, dalam proyek nyata saya menulis beberapa pseudocode daripada menulis kode persis seperti pada contoh saya. Setelah kodesemu, saya mencoba membagi tanggung jawab seperti yang saya lakukan dalam contoh. Bagaimanapun, begitulah cara saya melakukannya.
Emerson Cardoso
0

Dari buku Robert C. Martins Clean Architecture: A Craftsman Guide to Structure and Design Software , diterbitkan 10 September 2017, Robert menulis di halaman 62 berikut ini:

Secara historis, SRP telah dijelaskan dengan cara ini:

Modul harus memiliki satu, dan hanya satu, alasan untuk berubah

Sistem perangkat lunak diubah untuk memuaskan pengguna dan pemangku kepentingan; para pengguna dan pemangku kepentingan tersebut adalah "alasan untuk berubah". bahwa prinsipnya bicarakan. Memang, kita dapat menguraikan kembali prinsip untuk mengatakan ini:

Modul harus bertanggung jawab kepada satu, dan hanya satu, pengguna atau pemangku kepentingan

Sayangnya, kata "pengguna" dan "pemangku kepentingan" tidak benar-benar kata yang tepat untuk digunakan di sini. Kemungkinan akan ada lebih dari satu pengguna atau pemangku kepentingan yang menginginkan sistem berubah secara waras. Alih-alih, kami benar-benar merujuk pada grup - satu atau lebih orang yang memerlukan perubahan itu. Kami akan merujuk ke grup itu sebagai aktor .

Jadi versi terakhir dari SRP adalah:

Modul harus bertanggung jawab kepada satu, dan hanya satu, aktor.

Jadi ini bukan tentang kode. SRP adalah tentang mengendalikan aliran persyaratan dan kebutuhan bisnis, yang hanya dapat berasal dari satu sumber.

Benny Skogberg
sumber
Saya tidak yakin mengapa Anda membuat perbedaan bahwa "ini bukan tentang kode." Tentu saja tentang kode; ini adalah pengembangan perangkat lunak.
Robert Harvey
@RobertHarvey Maksud saya adalah aliran persyaratan berasal dari satu sumber, aktor. Pengguna dan Stakeholder tidak masuk ke dalam kode, mereka ke dalam aturan bisnis yang datang kepada kami sebagai persyaratan. Jadi SRP adalah proses untuk mengontrol persyaratan ini, yang bagi saya bukan kode. Ini Pengembangan Perangkat Lunak (!), Tetapi bukan kode.
Benny Skogberg