Apa tanggung jawab nyata sebuah kelas?

42

Saya terus bertanya-tanya apakah sah menggunakan kata kerja yang didasarkan pada kata benda di OOP.
Saya menemukan artikel yang brilian ini , meskipun saya masih tidak setuju dengan poin yang dibuatnya.

Untuk menjelaskan masalahnya sedikit lebih banyak, artikel itu menyatakan bahwa seharusnya tidak ada, misalnya, FileWriterkelas, tetapi karena menulis adalah tindakan, itu harus menjadi metode kelas File. Anda akan menyadari bahwa seringkali tergantung pada bahasa karena seorang programmer Ruby kemungkinan akan menentang penggunaan FileWriterkelas (Ruby menggunakan metode File.openuntuk mengakses file), sedangkan seorang programmer Java tidak akan melakukannya.

Pandangan pribadi saya (dan ya, sangat rendah hati) adalah dengan melakukan hal itu akan melanggar prinsip Tanggung Jawab Tunggal. Ketika saya diprogram dalam PHP (karena PHP jelas merupakan bahasa terbaik untuk OOP, kan?), Saya sering menggunakan kerangka kerja seperti ini:

<?php

// This is just an example that I just made on the fly, may contain errors

class User extends Record {

    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

}

class UserDataHandler extends DataHandler /* knows the pdo object */ {

    public function find($id) {
         $query = $this->db->prepare('SELECT ' . $this->getFields . ' FROM users WHERE id = :id');
         $query->bindParam(':id', $id, PDO::PARAM_INT);
         $query->setFetchMode( PDO::FETCH_CLASS, 'user');
         $query->execute();
         return $query->fetch( PDO::FETCH_CLASS );
    }


}

?>

Ini adalah pemahaman saya bahwa sufiks DataHandler tidak menambahkan sesuatu yang relevan ; tetapi intinya adalah bahwa prinsip tanggung jawab tunggal menentukan kita bahwa objek yang digunakan sebagai model yang berisi data (semoga disebut Rekaman) tidak seharusnya juga memiliki tanggung jawab untuk melakukan kueri SQL dan akses DataBase. Ini entah bagaimana membatalkan pola ActionRecord yang digunakan misalnya oleh Ruby on Rails.

Saya menemukan kode C # ini (yay, bahasa objek keempat yang digunakan dalam posting ini) beberapa hari yang lalu:

byte[] bytes = Encoding.Default.GetBytes(myString);
myString = Encoding.UTF8.GetString(bytes);

Dan saya harus mengatakan bahwa tidak masuk akal bagi saya bahwa kelas Encodingatau Charsetbenar-benar mengkodekan string. Seharusnya hanya menjadi representasi dari apa sebenarnya pengkodean itu.

Jadi, saya cenderung berpikir bahwa:

  • Bukan Filetanggung jawab kelas untuk membuka, membaca, atau menyimpan file.
  • Ini bukan Xmltanggung jawab kelas untuk membuat cerita bersambung sendiri.
  • Ini bukan Usertanggung jawab kelas untuk meminta basis data.
  • dll.

Namun, jika kita meramalkan ide-ide ini, mengapa Objectharus ada toStringkelas? Bukan tanggung jawab Mobil atau Anjing untuk mengubah dirinya menjadi string, bukan?

Saya mengerti bahwa dari sudut pandang pragmatis, menyingkirkan toStringmetode untuk keindahan mengikuti bentuk SOLID yang ketat, yang membuat kode lebih dapat dipertahankan dengan membuatnya tidak berguna, bukanlah pilihan yang dapat diterima.

Saya juga mengerti bahwa mungkin tidak ada jawaban yang pasti (yang lebih merupakan esai daripada jawaban yang serius) untuk ini, atau bahwa itu mungkin berdasarkan pendapat. Meskipun demikian saya masih ingin tahu apakah pendekatan saya benar-benar mengikuti apa sebenarnya prinsip tanggung jawab tunggal itu.

Apa tanggung jawab kelas?

Pierre Arlaud
sumber
Objek toString sebagian besar adalah kenyamanan, dan toString umumnya digunakan untuk dengan cepat melihat status internal selama debugging
ratchet freak
3
@ratchetfreak Saya setuju, tetapi itu adalah contoh (hampir lelucon) untuk menunjukkan bahwa tanggung jawab tunggal sangat sering rusak dengan cara yang saya jelaskan.
Pierre Arlaud
8
Meskipun prinsip tanggung jawab tunggal memiliki pendukung yang kuat, menurut saya itu adalah pedoman terbaik, dan sangat longgar untuk sebagian besar pilihan desain. Masalah dengan SRP adalah bahwa Anda berakhir dengan ratusan kelas yang diberi nama aneh, itu membuatnya sangat sulit untuk menemukan apa yang Anda butuhkan (atau untuk mengetahui apakah apa yang Anda butuhkan sudah ada) dan untuk memahami gambaran besarnya. Saya lebih suka memiliki lebih sedikit kelas-kelas ternama yang memberi tahu saya langsung apa yang diharapkan dari mereka walaupun mereka memiliki beberapa tanggung jawab. Jika di masa depan terjadi perubahan maka saya akan membagi kelas.
Dunk

Jawaban:

24

Mengingat beberapa perbedaan antara bahasa, ini bisa menjadi topik yang rumit. Jadi, saya merumuskan komentar-komentar berikut dengan cara yang mencoba menjadi selengkap yang saya bisa di dalam ranah OO.

Pertama-tama, apa yang disebut "Prinsip Tanggung Jawab Tunggal" adalah refleks - yang secara eksplisit dinyatakan - dari kohesi konsep . Membaca literatur waktu itu (sekitar '70), orang-orang (dan masih) berjuang untuk mendefinisikan apa modul itu, dan bagaimana membangunnya dengan cara yang akan melestarikan properti yang bagus. Jadi, mereka akan mengatakan "di sini ada banyak struktur dan prosedur, saya akan membuat modul dari mereka", tetapi tanpa kriteria mengapa seperangkat hal yang sewenang-wenang ini dikemas bersama-sama, organisasi tersebut mungkin berakhir tidak masuk akal. - sedikit "kohesi". Oleh karena itu, diskusi tentang kriteria muncul.

Jadi, hal pertama yang perlu diperhatikan di sini adalah, sejauh ini, perdebatannya seputar organisasi dan efek terkait pada pemeliharaan dan pemahaman (untuk masalah kecil ke komputer jika modul "masuk akal").

Kemudian, orang lain (mr. Martin) datang dan menerapkan pemikiran yang sama pada unit kelas sebagai kriteria untuk digunakan ketika memikirkan apa yang seharusnya atau tidak seharusnya menjadi miliknya, mempromosikan kriteria ini ke suatu prinsip, yang sedang dibahas sini. Maksudnya adalah bahwa "Kelas seharusnya hanya memiliki satu alasan untuk berubah" .

Nah, kita tahu dari pengalaman bahwa banyak objek (dan banyak kelas) yang tampaknya melakukan "banyak hal" memiliki alasan yang sangat bagus untuk melakukannya. Kasus yang tidak diinginkan adalah kelas-kelas yang membengkak dengan fungsi sampai tidak dapat ditembus untuk pemeliharaan, dll. Dan untuk memahami yang terakhir adalah untuk melihat di mana mr. Martin membidik ketika dia menguraikan masalah itu.

Tentu saja, setelah membaca apa mr. Martin menulis, harus jelas ini adalah kriteria untuk arahan dan desain untuk menghindari skenario yang bermasalah, tidak dengan cara apa pun untuk mengejar segala jenis kepatuhan, apalagi kepatuhan yang kuat, terutama ketika "tanggung jawab" tidak didefinisikan dengan jelas (dan pertanyaan seperti "apakah ini melanggar prinsip? "adalah contoh sempurna dari kebingungan luas). Jadi, saya merasa disayangkan itu disebut prinsip, menyesatkan orang untuk mencoba membawanya ke konsekuensi terakhir, di mana itu tidak ada gunanya. Tn. Martin sendiri membahas desain yang "melakukan lebih dari satu hal" yang mungkin harus dijaga seperti itu, karena pemisahan akan menghasilkan hasil yang lebih buruk. Juga, ada banyak tantangan yang diketahui tentang modularitas (dan hal ini merupakan kasusnya), kami tidak pada titik memiliki jawaban yang baik bahkan untuk beberapa pertanyaan sederhana tentang hal itu.

Namun, jika kita memperkirakan ide-ide ini, mengapa Object memiliki kelas toString? Bukan tanggung jawab Mobil atau Anjing untuk mengubah dirinya menjadi string, bukan?

Sekarang, izinkan saya berhenti sejenak untuk mengatakan sesuatu di sini tentang toString: ada hal mendasar yang umumnya diabaikan ketika seseorang membuat transisi pemikiran dari modul ke kelas dan merefleksikan metode apa yang seharusnya dimiliki kelas. Dan masalahnya adalah pengiriman dinamis (alias, akhir mengikat, "polimorfisme").

Dalam dunia tanpa "metode utama", memilih antara "obj.toString ()" atau "toString (obj)" adalah masalah preferensi sintaks saja. Namun, di dunia di mana programmer dapat mengubah perilaku program dengan menambahkan subclass dengan implementasi yang berbeda dari metode yang ada / diganti, pilihan ini tidak lebih dari rasa: membuat prosedur metode juga dapat menjadikannya kandidat untuk menimpa, dan hal yang sama mungkin tidak berlaku untuk "prosedur bebas" (bahasa yang mendukung multi-metode memiliki jalan keluar dari dikotomi ini). Konsekuensinya, ini bukan lagi diskusi tentang organisasi saja, tetapi pada semantik juga. Akhirnya, ke kelas mana metode ini terikat, juga menjadi keputusan yang berdampak (dan dalam banyak kasus, sejauh ini, kami memiliki sedikit lebih dari pedoman untuk membantu kami memutuskan di mana barang-barang berada,

Akhirnya, kita dihadapkan dengan bahasa yang membawa keputusan desain yang mengerikan, misalnya, memaksa seseorang untuk membuat kelas untuk setiap hal kecil. Jadi, apa yang dulunya merupakan alasan kanonik dan kriteria utama untuk memiliki objek (dan di kelas-tanah, oleh karena itu, kelas) sama sekali, yaitu, untuk memiliki "objek" yang semacam "perilaku yang juga berperilaku seperti data" , tetapi melindungi representasi konkret mereka (jika ada) dari manipulasi langsung dengan segala cara (dan itulah petunjuk utama untuk apa yang seharusnya menjadi antarmuka suatu objek, dari sudut pandang kliennya), menjadi kabur dan bingung.

Thiago Silva
sumber
31

[Catatan: Saya akan berbicara tentang benda-benda di sini. Objek adalah tujuan dari pemrograman berorientasi objek, bukan kelas.]

Apa tanggung jawab suatu objek sebagian besar tergantung pada model domain Anda. Biasanya ada banyak cara untuk memodelkan domain yang sama, dan Anda akan memilih satu cara atau yang lain berdasarkan pada bagaimana sistem akan digunakan.

Seperti yang kita semua tahu, metodologi "kata kerja / kata benda" yang sering diajarkan dalam kursus pengantar adalah konyol, karena sangat tergantung pada bagaimana Anda merumuskan kalimat. Anda dapat mengekspresikan hampir semua hal baik sebagai kata benda atau kata kerja, baik dengan suara aktif maupun pasif. Memiliki model domain Anda bergantung pada yang salah, itu harus menjadi pilihan desain sadar apakah sesuatu itu objek atau metode, bukan hanya konsekuensi tidak disengaja dari bagaimana Anda merumuskan persyaratan.

Namun, tidak menunjukkan bahwa, seperti Anda memiliki banyak kemungkinan untuk mengekspresikan hal yang sama dalam bahasa Inggris, Anda memiliki banyak kemungkinan untuk mengekspresikan hal yang sama dalam model domain Anda ... dan tidak ada kemungkinan mereka secara inheren lebih benar dari yang lain. Itu tergantung pada konteksnya.

Berikut adalah contoh yang juga sangat populer dalam kursus pengantar OO: rekening bank. Yang biasanya dimodelkan sebagai objek dengan balancebidang dan transfermetode. Dengan kata lain: saldo rekening adalah data yang , dan transfer adalah operasi . Yang merupakan cara yang masuk akal untuk memodelkan rekening bank.

Kecuali, itu bukan bagaimana rekening bank dimodelkan dalam perangkat lunak perbankan dunia nyata (dan pada kenyataannya, itu bukan cara bank bekerja di dunia nyata). Alih-alih, Anda memiliki slip transaksi, dan saldo akun dihitung dengan menambahkan (dan mengurangi) semua slip transaksi untuk akun. Dengan kata lain: transfer adalah data dan sisanya adalah operasi ! (Menariknya, ini juga membuat sistem Anda berfungsi murni, karena Anda tidak perlu mengubah saldo, baik objek akun Anda maupun objek transaksi tidak perlu diubah, mereka tidak dapat diubah.)

Mengenai pertanyaan spesifik Anda toString, saya setuju. Saya sangat suka solusi Haskell dari Showable kelas tipe . (Scala mengajarkan kita bahwa kelas tipe sangat cocok dengan OO.) Sama dengan kesetaraan. Kesetaraan sering kali bukan merupakan properti dari suatu objek tetapi dari konteks di mana objek tersebut digunakan. Coba pikirkan angka floating point: seperti apa epsilon itu? Sekali lagi, Haskell memiliki Eqkelas tipe.

Jörg W Mittag
sumber
Saya suka perbandingan haskell Anda, itu membuat jawabannya sedikit lebih ... segar. Apa yang akan Anda katakan tentang desain yang memungkinkan seorang ActionRecordmodel dan pemohon Database? Tidak peduli konteksnya tampaknya melanggar prinsip tanggung jawab tunggal.
Pierre Arlaud
5
"... karena itu sangat tergantung pada bagaimana kamu merumuskan sebuah kalimat." Yay! Oh dan saya suka contoh perbankan Anda. Tidak pernah tahu bahwa sepanjang jalan kembali pada tahun delapan puluhan saya sudah diajarkan pemrograman fungsional :) (desain sentris data dan penyimpanan waktu-independen di mana saldo dll dapat dihitung dan tidak boleh disimpan, dan alamat seseorang tidak boleh diubah, tetapi disimpan sebagai fakta baru) ...
Marjan Venema
1
Saya tidak akan mengatakan metodologi Verb / Noun itu "konyol". Saya akan mengatakan bahwa desain sederhana akan gagal dan sangat sulit untuk memperbaikinya. Karena, berlawanan dengan contoh, bukankah RESTful API metodologi Verb / Noun? Di mana, untuk tingkat kesulitan ekstra, kata kerjanya sangat terbatas?
user949300
@ user949300: Fakta bahwa himpunan kata kerja telah diperbaiki, tepatnya menghindari masalah yang saya sebutkan, yaitu bahwa Anda dapat merumuskan kalimat dalam berbagai cara, sehingga membuat kata benda dan kata kerja apa yang bergantung pada gaya penulisan dan bukan analisis domain .
Jörg W Mittag
8

Seringkali Prinsip Tanggung Jawab Tunggal menjadi Prinsip Tanggung Jawab Nol, dan Anda berakhir dengan Kelas Anemik yang tidak melakukan apa-apa (kecuali pemukim dan pengambil), yang mengarah ke bencana Kerajaan Noun .

Anda tidak akan pernah sempurna, tetapi lebih baik bagi kelas untuk melakukan terlalu banyak daripada terlalu sedikit.

Dalam contoh Anda dengan Pengkodean, IMO, itu pasti harus dapat menyandikan. Apa yang harus Anda lakukan sebagai gantinya? Hanya memiliki nama "utf" bukanlah tanggung jawab. Sekarang, mungkin namanya harus Encoder. Tetapi, seperti Konrad katakan, data (yang menyandikan) dan perilaku (melakukannya) milik bersama .

pengguna949300
sumber
7

Instance dari kelas adalah penutupan. Itu dia. Jika Anda berpikir seperti itu, semua perangkat lunak yang dirancang dengan baik akan Anda lihat akan terlihat benar, dan semua perangkat lunak yang dipikirkan dengan buruk tidak akan melakukannya. Biarkan saya berkembang.

Jika Anda ingin menulis sesuatu untuk ditulis ke file, pikirkan semua hal yang perlu Anda tentukan (ke OS): nama file, metode akses (baca, tulis, tambahkan), string yang ingin Anda tulis.

Jadi, Anda membangun objek File dengan nama file (dan metode akses). Objek File sekarang ditutup atas nama file (dalam hal ini mungkin akan dianggap sebagai nilai readonly / const). Contoh File sekarang siap untuk menerima panggilan ke metode "tulis" yang ditentukan di kelas. Ini hanya membutuhkan argumen string, tetapi dalam tubuh metode menulis, implementasinya, ada juga akses ke nama file (atau file handle yang dibuat darinya).

Sebuah instance dari kelas File oleh karena itu mengemas beberapa data menjadi semacam gumpalan komposit sehingga nantinya penggunaan instance itu lebih sederhana. Ketika Anda memiliki objek File, Anda tidak perlu khawatir tentang apa nama file itu, Anda hanya khawatir tentang string apa yang Anda masukkan sebagai argumen ke dalam panggilan tulis. Untuk mengulangi - objek File merangkum semua yang Anda tidak perlu tahu tentang ke mana sebenarnya string yang ingin Anda tulis.

Sebagian besar masalah yang ingin Anda selesaikan dilapisi dengan cara ini - hal-hal yang dibuat di depan, hal-hal yang dibuat pada awal setiap iterasi dari beberapa jenis proses, kemudian hal-hal yang berbeda dinyatakan dalam dua bagian dari jika, maka hal-hal yang dibuat pada mulai dari beberapa jenis sub loop, kemudian hal-hal dinyatakan sebagai beberapa bagian dari algoritma dalam loop itu, dll. Ketika Anda menambahkan lebih banyak fungsi panggilan ke stack, Anda melakukan kode yang lebih halus, tetapi setiap kali Anda akan memiliki mengepak data dalam lapisan yang lebih rendah di tumpukan menjadi semacam abstraksi yang bagus yang membuatnya mudah diakses. Lihat apa yang Anda lakukan menggunakan "OO" sebagai semacam kerangka kerja manajemen penutupan untuk pendekatan pemrograman fungsional.

Solusi pintas untuk beberapa masalah melibatkan keadaan objek yang bisa berubah, yang tidak begitu fungsional - Anda memanipulasi penutupan dari luar. Anda membuat beberapa hal di kelas Anda "global", setidaknya untuk ruang lingkup di atas. Setiap kali Anda menulis setter - cobalah untuk menghindarinya. Saya berpendapat ini adalah di mana Anda mendapat tanggung jawab dari kelas Anda, atau kelas lain di mana ia beroperasi, salah. Tapi jangan terlalu khawatir - kadang-kadang pragmatis untuk membangkitkan sedikit keadaan yang bisa berubah untuk menyelesaikan beberapa jenis masalah dengan cepat, tanpa terlalu banyak beban kognitif, dan benar-benar menyelesaikan sesuatu.

Jadi secara ringkas, untuk menjawab pertanyaan Anda - apa tanggung jawab sebenarnya dari kelas? Ini untuk menyajikan jenis platform, berdasarkan pada beberapa data yang mendasarinya, untuk mencapai jenis operasi yang lebih rinci. Ini untuk merangkum setengah dari data yang terlibat dalam operasi yang lebih jarang berubah daripada separuh lainnya dari data (Think currying ...). Misalnya nama file vs string untuk ditulis.

Seringkali enkapsulasi ini tampak dangkal seperti objek dunia nyata / objek dalam domain masalah tetapi jangan tertipu. Saat Anda membuat kelas Mobil, sebenarnya bukan mobil, itu kumpulan data yang membentuk platform untuk mencapai hal-hal tertentu yang mungkin ingin Anda lakukan di mobil. Membentuk representasi string (toString) adalah salah satu dari hal-hal itu - memuntahkan semua data internal itu. Ingatlah bahwa kadang-kadang kelas Mobil mungkin bukan kelas yang tepat bahkan jika domain masalahnya adalah tentang mobil. Berbeda dengan Kingdom of nouns, ini operasinya, kata kerja yang harus didasari oleh sebuah kelas.

Benediktus
sumber
4

Saya juga mengerti bahwa mungkin tidak ada jawaban yang pasti (yang lebih merupakan esai daripada jawaban yang serius) untuk ini, atau bahwa itu mungkin berdasarkan pendapat. Namun demikian saya masih ingin tahu apakah pendekatan saya benar-benar mengikuti apa prinsip tanggung jawab tunggal itu sebenarnya.

Meskipun demikian, itu mungkin tidak selalu membuat kode yang baik. Di luar fakta sederhana bahwa segala jenis aturan yang diikuti secara membabi buta mengarah ke kode yang buruk, Anda memulai dari premis yang tidak valid: objek (pemrograman) bukan objek (fisik).

Objek adalah satu set kumpulan data dan operasi yang kohesif (dan terkadang hanya satu dari keduanya). Sementara ini sering memodelkan objek dunia nyata, perbedaan antara model komputer dari sesuatu dan hal itu sendiri memerlukan perbedaan.

Dengan mengambil garis keras antara "kata benda" yang mewakili sesuatu dan hal-hal lain yang mengkonsumsinya, Anda akan bertentangan dengan manfaat utama pemrograman berorientasi objek (memiliki fungsi dan menyatakan bersama sehingga fungsi tersebut dapat melindungi invarian negara) . Lebih buruk lagi, Anda mencoba untuk mewakili dunia fisik dalam kode, yang seperti yang ditunjukkan sejarah, tidak akan berfungsi dengan baik.

Telastyn
sumber
4

Saya menemukan kode C # ini (yay, bahasa objek keempat yang digunakan dalam posting ini) beberapa hari yang lalu:

byte[] bytes = Encoding.Default.GetBytes(myString);
myString = Encoding.UTF8.GetString(bytes);

Dan saya harus mengatakan bahwa tidak masuk akal bagi saya bahwa kelas Encodingatau Charsetbenar-benar mengkodekan string. Seharusnya hanya menjadi representasi dari apa sebenarnya pengkodean itu.

Secara teori, ya, tapi saya pikir C # membuat kompromi yang dibenarkan demi kesederhanaan (sebagai lawan dari pendekatan Jawa yang lebih ketat).

Jika Encodinghanya mewakili (atau mengidentifikasi) pengkodean tertentu - katakanlah, UTF-8 - maka Anda jelas membutuhkan Encoderkelas juga, sehingga Anda dapat mengimplementasikannya GetBytes- tetapi kemudian Anda perlu mengelola hubungan antara Encoders dan Encodings, jadi kami berakhir dengan kebaikan kita

EncodersFactory.getDefaultFactory().createEncoder(new UTF8Encoding()).getBytes(myString)

sangat cemerlang dalam artikel yang Anda tautkan (baca bagus, ngomong-ngomong).

Jika saya mengerti Anda dengan baik, Anda bertanya di mana garisnya.

Nah, di sisi lain spektrum adalah pendekatan prosedural, tidak sulit untuk diterapkan dalam bahasa OOP dengan menggunakan kelas statis:

HelpfulStuff.GetUTF8Bytes(myString)

Masalah besar tentang hal itu adalah ketika penulis HelpfulStuffdaun dan saya duduk di depan monitor, saya tidak bisa mengetahui di mana GetUTF8Bytespenerapannya.

Apakah saya mencarinya di HelpfulStuff, Utils, StringHelper?

Tuhan bantu saya jika metode ini benar-benar diterapkan secara independen di ketiganya ... yang sering terjadi, yang diperlukan hanyalah bahwa pria sebelum saya tidak tahu di mana harus mencari fungsi itu, dan percaya tidak ada namun demikian, mereka mengambil satu lagi dan sekarang kami memilikinya berlimpah.

Bagi saya, desain yang tepat bukan tentang memuaskan kriteria abstrak, tetapi tentang kemudahan saya untuk melanjutkan setelah saya duduk di depan kode Anda. Sekarang bagaimana caranya

EncodersFactory.getDefaultFactory().createEncoder(new UTF8Encoding()).getBytes(myString)

nilai dalam aspek ini? Buruk juga. Ini bukan jawaban yang ingin saya dengar ketika saya berteriak kepada teman sekerja saya, "bagaimana cara saya menyandikan string ke urutan byte?" :)

Jadi saya akan pergi dengan pendekatan moderat dan menjadi liberal tentang tanggung jawab tunggal. Saya lebih suka memiliki Encodingkelas baik mengidentifikasi jenis pengkodean dan melakukan pengkodean yang sebenarnya: data tidak terpisahkan dari perilaku.

Saya pikir ini berlaku sebagai pola fasad, yang membantu melumasi persneling. Apakah pola yang sah ini melanggar SOLID? Pertanyaan terkait: Apakah pola Fasad melanggar SRP?

Perhatikan bahwa ini mungkin bagaimana cara byte yang dienkode diimplementasikan secara internal (di perpustakaan .NET). Kelas-kelas tidak terlihat secara publik dan hanya API "fasad" ini yang diekspos di luar. Tidak sulit untuk memverifikasi apakah itu benar, saya hanya terlalu malas untuk melakukannya sekarang :) Mungkin tidak, tetapi itu tidak membatalkan maksud saya.

Anda dapat memilih untuk menyederhanakan implementasi yang terlihat secara publik demi keramahan sambil mempertahankan implementasi kanonik yang lebih parah secara internal.

Konrad Morawski
sumber
1

Di Jawa abstraksi yang digunakan untuk merepresentasikan file adalah stream, beberapa stream bersifat searah (hanya baca atau tulis) yang lainnya bidirectional. Untuk Ruby, kelas File adalah abstraksi yang mewakili file OS. Jadi pertanyaannya menjadi apa tanggung jawabnya? Di Jawa, tanggung jawab FileWriter adalah menyediakan aliran byte searah yang terhubung ke file OS. Di Ruby, tanggung jawab File adalah menyediakan akses dua arah ke file sistem. Mereka berdua memenuhi SRP mereka hanya memiliki tanggung jawab yang berbeda.

Bukan tanggung jawab Mobil atau Anjing untuk mengubah dirinya menjadi string, bukan?

Tentu saja mengapa tidak? Saya berharap kelas Mobil sepenuhnya mewakili abstraksi Mobil. Jika masuk akal untuk abstraksi mobil untuk digunakan di mana string diperlukan maka saya sepenuhnya berharap kelas mobil untuk mendukung konversi ke string.

Untuk menjawab pertanyaan yang lebih besar secara langsung, tanggung jawab kelas adalah bertindak sebagai abstraksi. Pekerjaan itu mungkin rumit, tapi itu intinya, abstraksi harus menyembunyikan hal-hal rumit di balik antarmuka yang lebih mudah digunakan dan tidak terlalu rumit. SRP adalah pedoman desain sehingga Anda fokus pada pertanyaan "apa yang diwakili oleh abstraksi ini?". Jika beberapa perilaku berbeda diperlukan untuk sepenuhnya mengabstraksi konsep maka jadilah itu.

stonemetal
sumber
0

Kelas dapat memiliki banyak tanggung jawab. Setidaknya dalam OOP data-sentris klasik.

Jika Anda menerapkan prinsip tanggung jawab tunggal sepenuhnya, Anda mendapatkan desain yang berfokus pada tanggung jawab di mana pada dasarnya setiap metode non-pembantu milik kelasnya sendiri.

Saya pikir tidak ada yang salah dengan mengekstraksi sepotong kompleksitas dari kelas dasar seperti dalam kasus File dan FileWriter. Keuntungannya adalah bahwa Anda mendapatkan pemisahan kode yang jelas yang bertanggung jawab untuk operasi tertentu dan juga bahwa Anda dapat menimpa (subkelas) hanya sepotong kode daripada menimpa seluruh kelas dasar (misalnya File). Namun demikian, menerapkan hal itu secara sistematis tampaknya merupakan tugas berat bagi saya. Anda hanya mendapatkan lebih banyak kelas untuk ditangani dan untuk setiap operasi Anda perlu memanggil kelas masing-masing. Ini adalah fleksibilitas yang dikenakan biaya.

Kelas-kelas diekstrak harus memiliki nama yang sangat deskriptif berdasarkan pada operasi mereka mengandung, misalnya <X>Writer, <X>Reader, <X>Builder. Nama-nama seperti <X>Manager, <X>Handlertidak menggambarkan operasi sama sekali dan jika mereka dipanggil seperti itu karena mengandung lebih dari satu operasi, maka tidak jelas apa yang telah Anda capai. Anda telah memisahkan fungsionalitas dari datanya, Anda masih melanggar prinsip tanggung jawab tunggal bahkan di kelas yang diekstraksi, dan jika Anda mencari metode tertentu, Anda mungkin tidak tahu di mana mencarinya (jika dalam <X>atau <X>Manager).

Sekarang, kasus Anda dengan UserDataHandler berbeda karena tidak ada kelas dasar (kelas yang berisi data aktual). Data untuk kelas ini disimpan dalam sumber daya eksternal (database) dan Anda menggunakan api untuk mengaksesnya dan memanipulasi mereka. Kelas Pengguna Anda mewakili pengguna run-time, yang benar-benar sesuatu yang berbeda dari pengguna gigih dan pemisahan tanggung jawab (kekhawatiran) mungkin berguna di sini terutama jika Anda memiliki banyak logika terkait run-time-user.

Anda mungkin memberi nama UserDataHandler seperti itu karena pada dasarnya hanya ada fungsi di kelas itu dan tidak ada data nyata. Namun, Anda mungkin juga abstrak dari fakta itu dan menganggap data dalam basis data milik kelas. Dengan abstraksi ini, Anda dapat memberi nama kelas berdasarkan pada apa itu dan bukan pada apa yang dilakukannya. Anda bisa memberinya nama seperti UserRepository, yang cukup apik dan juga menyarankan penggunaan pola repositori .

iklim
sumber