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?
sumber
Jawaban:
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:
Atau:
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.IO
namespace: sana kita dapat menemukan berbagai macam aliran fisik (misalnyaFileStream
,MemoryStream
atauNetworkStream
) dan berbagai pembaca dan penulis (BinaryWriter
,TextWriter
) yang bekerja pada tingkat logis. Dengan memisahkan mereka dengan cara ini, kita menghindari ledakan combinatoric: bukan perluFileStreamTextWriter
,FileStreamBinaryWriter
,NetworkStreamTextWriter
,NetworkStreamBinaryWriter
,MemoryStreamTextWriter
, danMemoryStreamBinaryWriter
, Anda hanya menghubungkan penulis dan sungai dan Anda dapat memiliki apa yang Anda inginkan. Kemudian nanti kita dapat menambahkan, katakanlah, danXmlWriter
, tanpa perlu mengimplementasikannya kembali untuk memori, file, dan jaringan secara terpisah.sumber
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.
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.
sumber
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
SupportChinese
kode 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.
sumber
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.
sumber
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:
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:
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.
sumber
Dalam konferensi di Yale ini, Paman Bob memberikan contoh lucu ini :
Dia mengatakan bahwa
Employee
ada tiga alasan untuk berubah, tiga sumber persyaratan perubahan, dan memberikan penjelasan yang lucu dan tidak jelas ini , tetapi tetap ilustratif:Dia memberikan solusi ini yang memecahkan pelanggaran SRP, tetapi belum menyelesaikan pelanggaran DIP yang tidak ditampilkan dalam video.
sumber
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.
sumber
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.
sumber
"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:
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:
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.
sumber
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.
sumber
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:
Agaknya, Anda memiliki beberapa kelas yang sesuai dengan
CustomerCRUD
antarmuka (jika tidak, antarmuka tidak perlu), dan beberapa fungsido_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
Read
metode yang tersedia untuk itu. Tetapi Anda ingin menulis fungsido_query_stuff(customer: ???)
yang operator transparan pada tabel atau tampilan penuh; itu hanya menggunakanRead
metode, setelah semua.Jadi buat antarmuka
CustomerReader antarmuka publik {public Customer Read (customerID: int)}
dan faktor
CustomerCrud
antarmuka Anda sebagai: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
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
customer
bertipe<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,
CustomerReader
danCustomerWriter
? 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 :)sumber
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.
sumber
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:
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 .
sumber
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.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
self
ke daftar parameter dengan python;) - Saya menggunakanSRP
seperti 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 .
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 ;)
sumber
Apa yang saya coba lakukan untuk menulis kode yang mengikuti SRP:
Contoh:
Masalah: dapatkan dua angka dari pengguna, hitung jumlah mereka dan hasilkan hasilnya kepada pengguna:
Selanjutnya, cobalah untuk mendefinisikan tanggung jawab berdasarkan tugas yang perlu dilakukan. Dari ini, ekstrak kelas yang sesuai:
Kemudian, program refactored menjadi:
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.
sumber
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:
Jadi ini bukan tentang kode. SRP adalah tentang mengendalikan aliran persyaratan dan kebutuhan bisnis, yang hanya dapat berasal dari satu sumber.
sumber