Mengapa memiliki bidang pribadi, tidak cukup dilindungi?

265

Apakah visibilitas privatebidang kelas / properti / atribut berguna? Dalam OOP, cepat atau lambat, Anda akan membuat subkelas dari sebuah kelas dan dalam hal itu, baik untuk memahami dan mampu memodifikasi implementasi sepenuhnya.

Salah satu hal pertama yang saya lakukan ketika saya mensubclass sebuah kelas adalah mengubah banyak privatemetode menjadi protected. Namun, menyembunyikan detail dari dunia luar itu penting – jadi kita perlu protecteddan bukan adil public.

Pertanyaan saya adalah: Apakah Anda tahu tentang kasus penggunaan penting di mana privatealih-alih protectedmerupakan alat yang baik, atau apakah dua opsi " protected& public" sudah cukup untuk bahasa OOP?

Adam Libuša
sumber
236
Kepada para downvoters: Walaupun saya juga sangat tidak setuju dengan premis OP, saya menjawab pertanyaan ini karena sangat masuk akal dan layak untuk dijawab. Ya, OP perlu diberi tahu mengapa ini salah, tetapi cara untuk melakukannya adalah dengan menulis jawaban (atau menyarankan suntingan ke jawaban yang ada), bukan untuk mengundurkan diri hanya karena ia belum menemukan jawabannya sendiri.
Ixrec
18
Kelas turunan adalah bagian dari dunia luar.
CodesInChaos
20
Jangan lupa bahwa dilindungi tidak selalu berarti akses dikunci ke hierarki warisan. Di Jawa, itu memberikan akses tingkat paket juga.
berry120
8
Dosen saya sering berkata, "Ada hal-hal yang tidak akan saya ceritakan kepada anak-anak saya. Itu saya bidang pribadi saya."
SáT

Jawaban:

225

Karena seperti yang Anda katakan, protectedmasih memberi Anda kemampuan untuk "memodifikasi implementasi sepenuhnya". Itu tidak benar-benar melindungi apa pun di dalam kelas.

Mengapa kita peduli tentang "melindungi dengan tulus" hal-hal di dalam kelas? Karena kalau tidak, tidak mungkin untuk mengubah detail implementasi tanpa melanggar kode klien . Dengan kata lain, orang yang menulis subclass juga "dunia luar" bagi orang yang menulis kelas dasar asli.

Dalam praktiknya, protectedanggota pada dasarnya adalah "API publik untuk subkelas" kelas dan harus tetap stabil dan kompatibel dengan belakang seperti halnya publicanggota. Jika kami tidak memiliki kemampuan untuk membuat privateanggota sejati , maka tidak ada dalam implementasi yang akan aman untuk diubah, karena Anda tidak akan dapat mengesampingkan kemungkinan bahwa kode klien (tidak berbahaya) entah bagaimana berhasil bergantung pada saya t.

Secara kebetulan, sementara "Dalam OOP, cepat atau lambat, Anda akan membuat subkelas kelas" secara teknis benar, argumen Anda tampaknya membuat asumsi yang jauh lebih kuat bahwa "cepat atau lambat, Anda akan membuat subkelas dari setiap kelas "yang hampir pasti bukan itu masalahnya.

Ixrec
sumber
11
Saya akan melemparkan Anda lebih dari +1 jika saya bisa, karena ini adalah pertama kalinya privatebenar-benar masuk akal bagi saya. Perspektif lib perlu lebih sering digunakan, jika tidak, siapa pun yang menyukai mentalitas "kendali absolut, tanggung jawab absolut" (seperti C) mungkin menandainya sebagai "melindungi saya dari diri saya sendiri". Perhatikan bahwa saya masih menggunakan privatesebelumnya, saya selalu merasa dokumentasi yang bagus dan konvensi penamaan ingin _foomenunjukkan Anda mungkin tidak boleh mengacaukannya itu setara jika tidak lebih baik. Mampu secara deterministik mengatakan "tidak ada yang akan rusak" adalah fitur yang sah, privatehanya.
abluejelly
Saya awalnya mengabaikan kasus kode perpustakaan umum dan kerangka kerja dan berpikir kurang lebih hanya dalam hal "kode klien". Mengoptimalkan implementasi internal adalah contoh yang baik untuk pertanyaan saya, meskipun saya bertanya pada diri sendiri, apakah ini benar-benar terjadi dalam kenyataan (terutama ketika banyak orang merekomendasikan suatu kelas tidak boleh lebih dari 1000 baris kode). Secara umum, saya suka pendekatan Ruby, di mana pribadi adalah semacam rekomendasi: "Ini naga, lanjutkan dengan hati-hati".
Adam Libuša
9
@ AdamLibuša Meskipun ini adalah kesepakatan yang jauh lebih besar jika kode Anda bersifat publik, kode ini tetap berlaku bahkan jika Anda adalah penulis dari semua klien kelas. Masalahnya hanya berubah dari refactor tertentu menjadi tidak mungkin menjadi refactor yang membosankan dan rentan kesalahan. Optimalisasi sebenarnya adalah alasan paling tidak umum untuk refactor ini dalam pengalaman saya (meskipun saya terutama melakukan Javascript), biasanya itu lebih seperti bug yang mengekspos cacat implementasi mendasar yang membutuhkan restrukturisasi ketergantungan / grafik panggilan dari berbagai bit internal untuk mencapai perbaikan yang benar-benar kuat.
Ixrec
5
"" Cepat atau lambat, Anda akan membuat subkelas dari setiap kelas "hampir pasti tidak demikian." Dan lebih tepatnya, Anda hampir pasti tidak, dan Anda HARUS tidak, mengesampingkan setiap fungsi dalam suatu kelas, dan mengubah penggunaan setiap elemen data dalam suatu kelas. Beberapa hal harus secara logis ditulis dalam batu agar kelas memiliki makna.
Jay
1
Saya akan melemparkan Anda lebih dari +1 jika saya bisa => Itulah yang kami sebut karunia @abluejelly
Thomas Ayoub
256

Di OOP, cepat atau lambat, Anda akan membuat subkelas kelas

Ini salah. Tidak setiap kelas dimaksudkan untuk subkelas dan beberapa bahasa OOP yang diketik secara statis bahkan memiliki fitur untuk mencegahnya, misalnya, final(Java dan C ++) atau sealed(C #).

itu baik untuk memahami dan dapat memodifikasi implementasi sepenuhnya.

Tidak, tidak. Baik bagi kelas untuk dapat dengan jelas mendefinisikan antarmuka publiknya dan melestarikan invariannya meskipun itu diwarisi darinya.

Secara umum, kontrol akses adalah tentang kompartementalisasi. Anda ingin satu bagian dari kode dipahami tanpa harus memahami secara detail bagaimana ia berinteraksi dengan bagian kode yang lain. Akses pribadi memungkinkan itu. Jika semuanya setidaknya dilindungi, Anda harus memahami apa yang dilakukan setiap subclass untuk memahami cara kerja kelas dasar.

Atau dengan kata-kata Scott Meyers: bagian pribadi dari suatu kelas dipengaruhi oleh sejumlah kode: kode kelas itu sendiri.

Bagian publik berpotensi dipengaruhi oleh setiap bit kode yang ada, dan setiap bit kode belum ditulis, yang merupakan jumlah kode yang tak terbatas.

Bagian yang dilindungi berpotensi dipengaruhi oleh setiap subclass yang ada, dan setiap subclass yang belum ditulis, yang juga merupakan jumlah kode yang tak terbatas.

Kesimpulannya adalah bahwa dilindungi memberi Anda sedikit lebih banyak daripada publik, sedangkan pribadi memberi Anda peningkatan nyata. Keberadaan specifier akses yang dilindungi dipertanyakan, bukan pribadi.

Sebastian Redl
sumber
34
+1 untuk skenario teoretis "dipengaruhi oleh jumlah kode yang tak terbatas"
Broken_Window
13
Mungkin patut dicatat juga bahwa desainer C # benar-benar memutuskan untuk melarang mengganti metode warisan kecuali jika secara eksplisit ditandai sebagai virtual, untuk alasan yang diuraikan dalam jawaban ini.
Will Vousden
11
Atau sederhananya: protecteduntuk metode, bukan untuk anggota data.
Matthieu M.
7
Saya tidak dapat menemukannya sekarang, tetapi saya ingat pernah membaca jawaban yang ditulis dengan baik dari @EricLippert tentang mengapa MS sealedbagian besar dari perpustakaan .net dan IIRC mengapa ia suka mengunci sisanya. Saat Anda mengizinkan pewaris pihak ketiga untuk mulai menyentuh nilai-nilai internal Anda perlu menambahkan sejumlah besar cek validasi / kewarasan untuk setiap metode karena Anda tidak bisa lagi mempercayai invarian desain tentang objek internal keadaan.
Dan Neely
4
@ ThorbjørnRavnAndersen "sambil mengembangkan tetapi tidak ketika dirilis" - bagaimana mungkin sebuah kelas harus tahu? Apakah Anda benar-benar ingin mengkompilasi versi uji yang berbeda dari versi yang dirilis ketika Anda bahkan tidak dapat menguji versi rilis? Tes seharusnya tidak perlu mengakses barang-barang pribadi.
Sebastian Redl
33

Ya, bidang pribadi mutlak diperlukan. Baru minggu ini saya perlu menulis implementasi kamus khusus di mana saya mengontrol apa yang dimasukkan ke dalam kamus. Jika bidang kamus dibuat dilindungi atau publik, maka kontrol yang saya tulis dengan hati-hati bisa dengan mudah dielakkan.

Bidang pribadi biasanya tentang memberikan perlindungan bahwa data sesuai dengan yang diharapkan oleh pembuat kode asli. Jadikan semuanya terlindungi / publik dan Anda menunggangi pelatih dan kuda melalui prosedur dan validasi tersebut.

Robbie Dee
sumber
2
Memberi +1 untuk "menumpang pelatih dan menunggang kuda" apa pun. Ini adalah ungkapan yang hebat yang saya harap saya mendengar lebih banyak.
Nic Hartley
2
Jika seseorang perlu membuat subkelas kelas Anda, mungkin ia harus memiliki akses ke perlindungan Anda? Dan jika Anda tidak ingin seseorang mengubah perlindungan Anda untuk mencapai beberapa tujuan, mungkin tidak membagikan kodenya? Ini berfungsi seperti ini di Ruby - private kurang lebih merupakan rekomendasi.
Adam Libuša
3
@ AdamLibuša "Jangan bagikan kode"? Segera setelah Anda menerbitkan DLL sama sekali, Anda membagikan kode - semua struktur dan metode dan semuanya ada untuk dilihat seluruh dunia, terutama dengan bahasa yang mendukung refleksi secara default. Semua orang dapat melakukan apa pun yang mereka inginkan dengan "kode Anda" - privatehanyalah cara untuk mengatakan "jangan sentuh ini", dan minta diberlakukan dalam kontrak kompiler. Dalam sistem seperti .NET, ini juga memiliki implikasi keamanan yang penting - Anda hanya dapat menyentuh privasi orang lain ketika Anda memiliki kepercayaan penuh (pada dasarnya setara dengan admin / akses root).
Luaan
2
@ AdamLibuša Saya pikir kebingungan Anda terutama berasal dari pendekatan OOP yang berbeda yang diambil oleh bahasa yang berbeda. Akar OOP (seperti yang didefinisikan sebelumnya) adalah olahpesan - yang berarti bahwa semuanya bersifat pribadi, kecuali untuk pesan yang Anda tanggapi. Di sebagian besar bahasa OOPish, ini diekspos sebagai "jaga kerahasiaan data Anda" - cara untuk membuat antarmuka publik sekecil mungkin. Satu-satunya cara pengguna (baik itu subclass atau kelas lain) harus memanipulasi kelas Anda adalah melalui antarmuka publik yang Anda tetapkan - mirip dengan cara Anda biasanya menggunakan roda kemudi dan pedal untuk mengendarai mobil Anda :)
Luaan
3
@Luaan +1 ketika tidak apa-apa menyentuh privasi orang lain!
Nigel Touch
12

Ketika mencoba memberi alasan secara formal tentang kebenaran program Berorientasi Objek , biasanya menggunakan pendekatan modular yang melibatkan invarian objek . Dalam pendekatan ini

  1. Metode telah dikaitkan dengan mereka sebelum dan sesudah kondisi (kontrak).
  2. Objek telah dikaitkan dengan mereka invarian.

Penalaran modular tentang suatu objek berlangsung sebagai berikut (setidaknya untuk perkiraan pertama)

  1. Buktikan bahwa konstruktor objek menetapkan invarian
  2. Untuk setiap metode non-pribadi, anggap objek invarian dan metode prekondisi ditahan pada entri, lalu buktikan bahwa tubuh kode menyiratkan bahwa postcondition dan invarian memegang pada metode keluar

Bayangkan kami memverifikasi dan object Amenggunakan pendekatan di atas. Dan sekarang ingin memverifikasi method gdari object Byang panggilan method fdari object A. Modular reasoning memungkinkan kita untuk berpikir tentang method gtanpa harus mempertimbangkan kembali implementasi method f. Asalkan kita dapat menetapkan invarian object Adan prasyarat method fdi situs panggilan di method g,kita dapat mengambil kondisi posting method fsebagai ringkasan perilaku panggilan metode. Selain itu kita juga akan tahu bahwa setelah panggilan kembali invarian Amasih memegang.

Modularitas penalaran inilah yang memungkinkan kita berpikir secara formal tentang program-program besar. Kita dapat memberi alasan tentang masing-masing metode secara individu dan kemudian menyusun hasil dari alasan ini pada gilirannya menjadi alasan tentang bagian-bagian yang lebih besar dari program.

Bidang pribadi sangat berguna dalam proses ini. Untuk mengetahui bahwa invarian objek terus bertahan di antara dua pemanggilan metode pada objek tersebut, kami biasanya bergantung pada fakta bahwa objek tersebut tidak dimodifikasi dalam periode intervensi.

Untuk penalaran modular untuk bekerja dalam konteks di mana objek tidak memiliki bidang pribadi maka kita harus memiliki beberapa cara untuk memastikan bahwa apa pun bidang yang kebetulan diatur ke objek lain, bahwa invarian selalu didirikan kembali (setelah bidang set). Sulit membayangkan objek invarian yang keduanya memegang tidak peduli apa nilai bidang objek, dan juga berguna dalam penalaran tentang kebenaran program. Kami mungkin harus membuat beberapa konvensi yang rumit di sekitar akses lapangan. Dan mungkin juga kehilangan sebagian (paling buruk bahkan semua) kemampuan kita untuk berpikir secara modular.

Bidang yang dilindungi

Bidang yang dilindungi mengembalikan sebagian kemampuan kami untuk bernalar secara modular. Tergantung pada bahasanya protectedmungkin membatasi kemampuan untuk mengatur bidang ke semua subclass atau semua subclass dan kelas paket yang sama. Sering kali kita tidak memiliki akses ke semua subclass ketika kita beralasan tentang kebenaran suatu objek yang kita tulis. Misalnya, Anda mungkin menulis komponen atau pustaka yang nantinya akan digunakan dalam program yang lebih besar (atau beberapa program yang lebih besar) - beberapa di antaranya bahkan mungkin belum ditulis. Biasanya Anda tidak akan tahu jika dan dengan cara apa itu mungkin sub-kelas.

Namun, biasanya ada kewajiban pada subclass untuk mempertahankan objek invarian dari kelas yang diperluas. Jadi, dalam bahasa di mana proteksi hanya berarti "sub-kelas", dan di mana kami didisiplinkan untuk memastikan bahwa sub-kelas selalu mempertahankan invarian dari superclass mereka, Anda dapat berargumen bahwa pilihan menggunakan yang dilindungi alih-alih swasta hanya kehilangan modularitas minimal .

Meskipun saya telah berbicara tentang penalaran formal, sering kali berpikir bahwa ketika programmer secara informal memberikan alasan tentang kebenaran kode mereka, mereka kadang-kadang juga bergantung pada jenis argumen yang serupa.

flamingpenguin
sumber
8

privatevariabel dalam kelas lebih baik daripada protecteduntuk alasan yang sama bahwa breakpernyataan di dalam switchblok lebih baik daripada goto labelpernyataan; yaitu bahwa pemrogram manusia cenderung melakukan kesalahan.

protectedvariabel meminjamkan diri mereka untuk penyalahgunaan yang tidak disengaja (kesalahan programmer), seperti gotopernyataan yang cocok untuk pembuatan kode spaghetti.

Apakah mungkin untuk menulis kode bebas bug yang berfungsi menggunakan protectedvariabel kelas? Ya tentu saja! Sama seperti mungkin untuk menulis kode bebas bug menggunakan menggunakan goto; tetapi sebagaimana klise berbunyi, "Hanya karena Anda bisa, tidak berarti Anda harus melakukannya!"

Kelas, dan memang paradigma OO, ada untuk menjaga terhadap programmer manusia yang rentan kesalahan membuat kesalahan. Pertahanan melawan kesalahan manusia hanya sebagus tindakan defensif yang dibangun di dalam kelas. Membuat implementasi kelas Anda protectedsama dengan meniup lubang besar di dinding benteng.

Kelas dasar sama sekali tidak memiliki pengetahuan tentang kelas turunan. Sejauh menyangkut kelas dasar, protectedsebenarnya tidak memberi Anda lebih banyak perlindungan daripada public, karena tidak ada yang menghentikan kelas turunan dari membuat publicpengambil / penyetel yang berperilaku seperti pintu belakang.

Jika kelas dasar mengizinkan akses tanpa hambatan ke detail implementasi internalnya, maka tidak mungkin bagi kelas itu sendiri untuk mempertahankan diri dari kesalahan. Kelas dasar sama sekali tidak memiliki pengetahuan tentang kelas turunannya, dan oleh karena itu tidak memiliki cara untuk menjaga terhadap kesalahan yang dibuat dalam kelas turunan tersebut.

Hal terbaik yang bisa dilakukan oleh kelas dasar adalah menyembunyikan sebanyak mungkin implementasinya privatedan memberlakukan batasan yang cukup untuk menjaga agar tidak melanggar perubahan dari kelas turunan atau apa pun di luar kelas.

Pada akhirnya, ada bahasa tingkat tinggi untuk meminimalkan kesalahan manusia. Praktik pemrograman yang baik (seperti prinsip SOLID ) juga ada untuk meminimalkan kesalahan manusia.

Pengembang perangkat lunak yang mengabaikan praktik pemrograman yang baik memiliki kemungkinan kegagalan yang jauh lebih tinggi, dan lebih mungkin menghasilkan solusi yang tidak dapat dipecahkan yang rusak. Mereka yang mengikuti praktik-praktik yang baik memiliki kemungkinan kegagalan yang jauh lebih rendah, dan lebih cenderung menghasilkan solusi yang dapat dipertahankan bekerja.

Ben Cottrell
sumber
Untuk downvoter - apa yang bisa diubah / ditingkatkan tentang jawaban ini?
Ben Cottrell
Saya tidak memilih, tetapi membandingkan tingkat akses dengan break / goto?
imel96
@ imel96 Tidak, membandingkan alasan mengapa mereka dihindari, dan tidak disarankan oleh "Praktik terbaik" (terutama untuk menulis kode baru ). mis. Seorang programmer yang kompeten akan menghindari publicdetail implementasi karena itu cocok untuk kode yang tidak dapat dipertahankan. Seorang programmer yang kompeten akan menghindari gotokarena cocok untuk kode yang tidak dapat dipertahankan. Namun dunia nyata sedemikian rupa sehingga kadang-kadang Anda tersesat dalam kekacauan kode warisan yang mengerikan, dan tidak punya pilihan selain menggunakan goto, dan untuk alasan yang sama Anda kadang-kadang tidak punya pilihan selain menggunakan detail implementasi publik / dilindungi.
Ben Cottrell
Tingkat keparahannya berbeda dalam besarnya, tidak ada bandingannya. Saya bisa hidup dengan kode yang hanya memiliki publicproperti / antarmuka, tetapi tidak dengan kode yang hanya menggunakan goto.
imel96
2
@ imel96 Pernahkah Anda benar-benar bekerja dalam kode yang menggunakan gotoatau hanya karena Anda telah membaca artikel seperti itu? Saya mengerti mengapa goto itu jahat karena saya telah menghabiskan 2 tahun bekerja dalam kode kuno yang menggunakannya; dan saya telah menghabiskan lebih lama lagi bekerja di kode di mana "kelas" memiliki detail implementasi mereka bocor di mana-mana dengan publicdan friendspecifier digunakan sebagai peretasan cepat / mudah / kotor. Saya tidak menerima argumen bahwa "mengekspos detail implementasi secara publik" tidak dapat menyebabkan kekacauan spaghetti yang saling terkait pada tingkat keparahan yang sama gotokarena itu benar-benar bisa.
Ben Cottrell
4

Kelas yang tidak dapat diwarisi memiliki dua kontrak - satu dengan pemegang referensi objek, dan dengan kelas turunan. Anggota publik terikat oleh kontrak dengan pemegang referensi, dan anggota yang dilindungi terikat oleh kontrak dengan kelas turunan.

Membuat anggota protectedmenjadikannya kelas dasar yang lebih fleksibel, tetapi seringkali akan membatasi cara perubahan versi kelas di masa depan. Membuat anggota privatememungkinkan penulis kelas lebih fleksibel untuk mengubah cara kerja kelas, tetapi membatasi jenis kelas yang dapat bermanfaat berasal darinya.

Sebagai contoh, List<T>di .NET membuat backing store menjadi pribadi; jika dilindungi, tipe turunan dapat melakukan beberapa hal berguna yang jika tidak tidak mungkin, tetapi versi masa depan List<T>akan selamanya harus menggunakan toko dukungan monolitik yang kikuk bahkan untuk daftar yang menyimpan jutaan item. Menjadikan toko dukungan pribadi akan memungkinkan versi masa depan List<T>untuk menggunakan toko dukungan lebih efisien tanpa melanggar kelas turunan.

supercat
sumber
4

Saya pikir ada asumsi kunci dalam argumen Anda bahwa ketika seseorang menulis kelas mereka tidak tahu siapa yang mungkin memperluas kelas itu di jalan dan untuk alasan apa . Dengan asumsi ini, argumen Anda akan masuk akal karena setiap variabel yang Anda buat bersifat pribadi maka berpotensi memotong jalan pengembangan. Namun, saya akan menolak anggapan itu.

Jika asumsi itu ditolak maka hanya ada dua kasus untuk dipertimbangkan.

  1. Penulis kelas asli memiliki ide yang sangat jelas mengapa dapat diperluas (misalnya BaseFoo dan akan ada beberapa implementasi Foo konkret di jalan).

Dalam hal ini, penulis tahu bahwa seseorang akan memperluas kelas dan mengapa dan oleh karena itu akan tahu persis apa yang harus dilindungi dan apa yang harus dijadikan rahasia. Mereka menggunakan perbedaan pribadi / terlindungi untuk mengkomunikasikan semacam antarmuka kepada pengguna yang membuat subkelas.

  1. Penulis kelas anak mencoba untuk meretas beberapa perilaku ke dalam kelas induk.

Kasing ini harus jarang (Anda bisa berargumen bahwa itu tidak sah), dan tidak disukai hanya memodifikasi kelas asli dalam basis kode asli. Ini juga bisa menjadi gejala desain yang buruk. Dalam kasus itu saya lebih suka orang yang melakukan peretasan hanya menggunakan peretasan lain seperti teman (C / C ++) dan setAccessible(true)(Jawa).

Saya pikir aman untuk menolak asumsi itu.

Ini umumnya jatuh kembali ke ide komposisi di atas warisan . Warisan sering diajarkan sebagai cara yang ideal untuk mengurangi penggunaan kembali kode namun itu jarang menjadi pilihan pertama untuk penggunaan kembali kode. Saya tidak memiliki argumen knock-down sederhana dan itu bisa menjadi hal yang cukup sulit dan kontroversial untuk dipahami. Namun, dalam pengalaman saya dengan pemodelan domain saya telah menemukan bahwa saya jarang menggunakan warisan tanpa memiliki pemahaman yang sangat jelas tentang siapa yang akan mewarisi kelas saya dan mengapa.

Kecepatan
sumber
2

Ketiga tingkat akses memiliki kasus penggunaannya, OOP akan menjadi tidak lengkap karena kekurangan salah satu dari mereka. Biasanya Anda melakukannya

  • jadikan semua variabel / anggota data pribadi . Anda tidak ingin seseorang dari luar mengacaukan data internal Anda. Juga metode yang menyediakan fungsionalitas tambahan (pikirkan perhitungan berdasarkan beberapa variabel anggota) untuk antarmuka publik atau Anda yang dilindungi - ini hanya untuk penggunaan internal, dan Anda mungkin ingin mengubahnya / memperbaikinya di masa mendatang.
  • buat antarmuka umum kelas Anda menjadi publik . Itulah yang seharusnya digunakan oleh pengguna kelas asli Anda, dan bagaimana menurut Anda seharusnya kelas turunan juga. Untuk memberikan enkapsulasi yang tepat, ini biasanya hanya metode (dan kelas / struct pembantu, enum, typedef, apa pun yang dibutuhkan pengguna untuk bekerja dengan metode Anda), bukan variabel.
  • mendeklarasikan metode yang dilindungi yang dapat berguna bagi seseorang yang ingin memperluas / mengkhususkan fungsionalitas kelas Anda, tetapi tidak boleh menjadi bagian dari antarmuka publik - sebenarnya Anda biasanya membesarkan anggota pribadi untuk dilindungi ketika diperlukan. Jika ragu Anda tidak, sampai Anda tahu itu
    1. kelas Anda dapat / mungkin / akan disubklasifikasikan,
    2. dan memiliki gagasan yang jelas tentang apa kasus penggunaan subclassing.

Dan Anda menyimpang dari skema umum ini hanya jika ada alasan yang bagus ™ . Waspadalah terhadap "ini akan membuat hidup saya lebih mudah ketika saya dapat dengan bebas mengaksesnya dari luar" (dan di luar sini juga termasuk subclass). Ketika saya menerapkan hierarki kelas, saya sering mulai dengan kelas yang tidak memiliki anggota yang dilindungi, sampai saya datang untuk subklasifikasi / memperluas / mengkhususkan mereka, menjadi kelas dasar dari framework / toolkit dan kadang-kadang memindahkan bagian dari fungsi asli mereka satu tingkat ke atas.

Murphy
sumber
1

Mungkin pertanyaan yang lebih menarik adalah mengapa diperlukan bidang jenis lain selain pribadi. Ketika sebuah subclass perlu berinteraksi dengan data superclass, melakukan hal itu secara langsung membuat kopling langsung antara keduanya, sedangkan menggunakan metode untuk menyediakan interaksi antara keduanya memungkinkan tingkat tipuan yang dapat memungkinkan untuk membuat perubahan pada superclass yang seharusnya sangat sulit.

Sejumlah bahasa (mis. Ruby dan Smalltalk) tidak menyediakan bidang publik sehingga pengembang tidak diperbolehkan untuk memungkinkan penggandengan langsung ke implementasi kelas mereka, tetapi mengapa tidak melangkah lebih jauh dan hanya memiliki bidang pribadi? Tidak akan ada kerugian umum (karena superclass selalu dapat menyediakan accessors yang dilindungi untuk subclass), tetapi akan memastikan bahwa kelas selalu memiliki setidaknya tingkat isolasi kecil dari subclass mereka. Mengapa ini bukan desain yang lebih umum?

Jules
sumber
1

Banyak jawaban bagus di sini, tapi saya akan tetap menggunakan dua sen saya. :-)

Pribadi baik untuk alasan yang sama bahwa data global buruk.

Jika suatu kelas mendeklarasikan data pribadi, maka Anda benar-benar tahu bahwa satu-satunya kode yang mengacaukan data ini adalah kode di dalam kelas. Ketika ada bug, Anda tidak perlu mencari di seluruh kreasi untuk menemukan setiap tempat yang dapat mengubah data ini. Anda tahu itu di kelas. Saat Anda membuat perubahan pada kode, dan Anda mengubah sesuatu tentang bagaimana bidang ini digunakan, Anda tidak harus melacak semua tempat potensial yang mungkin menggunakan bidang ini dan mempelajari apakah perubahan yang Anda rencanakan akan menghancurkannya. Anda tahu satu-satunya tempat di dalam kelas.

Saya sudah berkali-kali harus membuat perubahan pada kelas yang ada di perpustakaan dan digunakan oleh banyak aplikasi, dan saya harus menginjak dengan sangat hati-hati untuk memastikan saya tidak merusak beberapa aplikasi yang tidak saya ketahui. Semakin banyak data publik dan yang dilindungi, semakin besar potensi masalah.

Jay
sumber
1

Saya pikir perlu menyebutkan beberapa pendapat yang berbeda pendapat.

Secara teori, ada baiknya mengontrol tingkat akses untuk semua alasan yang disebutkan dalam jawaban lain.

Dalam praktiknya, terlalu sering ketika meninjau kode, saya melihat orang-orang (yang suka menggunakan pribadi), mengubah tingkat akses dari pribadi -> dilindungi dan tidak terlalu sering dari dilindungi -> publik. Hampir selalu, mengubah properti kelas melibatkan memodifikasi setter / getter. Ini telah menyita banyak waktu saya (review kode) dan mereka (mengubah kode).

Ini juga mengganggu saya bahwa itu berarti kelas mereka tidak Ditutup untuk modifikasi.

Itu dengan kode internal di mana Anda selalu dapat mengubahnya jika perlu juga. Situasi lebih buruk dengan kode pihak ke-3 ketika tidak mudah untuk mengubah kode.

Jadi, berapa banyak programmer yang menganggapnya merepotkan? Nah, berapa banyak yang menggunakan bahasa pemrograman yang tidak memiliki privat? Tentu saja, orang tidak hanya menggunakan bahasa-bahasa itu karena mereka tidak memiliki specifier swasta, tetapi membantu untuk menyederhanakan bahasa dan kesederhanaan itu penting.

Imo itu sangat mirip dengan pengetikan dinamis / statis. Secara teori, pengetikan statis sangat baik. Dalam prakteknya, itu hanya mencegah seperti 2% dari kesalahan tersebut tidak masuk akal Efektivitas Dinamis Mengetik ... . Menggunakan pribadi mungkin mencegah kesalahan kurang dari itu.

Saya pikir prinsip-prinsip SOLID baik, saya berharap orang lebih peduli tentang mereka daripada mereka peduli menciptakan kelas dengan publik, dilindungi dan pribadi.

imel96
sumber
1
Jika Anda perlu mengubah kode pihak ke-3 saat menggunakannya, kode tersebut tidak dirancang dengan baik, atau Anda tidak menggunakannya kembali dengan baik. Anda selalu dapat menggunakan kembali kelas non-abstrak dengan mengenkapsulasinya, tetapi dalam praktiknya Anda hampir tidak perlu mensubklasifikasikannya.
Little Santi
0

Saya juga ingin menambahkan contoh praktis lain mengapa protectedtidak cukup. Di universitas saya tahun-tahun pertama melakukan proyek di mana mereka harus mengembangkan versi desktop dari permainan papan (yang kemudian dikembangkan untuk AI dan terhubung ke pemain lain melalui jaringan). Beberapa kode parsial disediakan bagi mereka untuk memulainya termasuk kerangka pengujian. Beberapa properti dari kelas game utama diekspos protectedsehingga kelas tes yang memperluas kelas ini memiliki akses ke mereka. Tetapi bidang ini bukan informasi sensitif.

Sebagai TA untuk unit saya sering melihat siswa hanya membuat semua kode tambahan mereka protectedatau public(mungkin karena mereka melihat yang lain protecteddan publicbarang-barang dan menganggap mereka harus mengikuti). Saya bertanya kepada mereka mengapa tingkat perlindungan mereka tidak pantas, dan banyak yang tidak tahu mengapa. Jawabannya adalah bahwa informasi sensitif yang mereka paparkan ke subclass berarti bahwa pemain lain dapat menipu dengan hanya memperluas kelas itu dan mengakses informasi yang sangat sensitif untuk permainan (pada dasarnya lokasi tersembunyi lawan, saya kira mirip dengan bagaimana jadinya jika Anda bisa melihat potongan lawan Anda di papan kapal perang dengan memperpanjang beberapa kelas). Itu membuat kode mereka sangat berbahaya dalam konteks permainan.

Selain itu, ada banyak alasan lain untuk menjaga sesuatu tetap pribadi bahkan untuk subkelas Anda. Mungkin menyembunyikan detail implementasi yang dapat mengacaukan kerja kelas dengan benar jika diubah oleh seseorang yang tidak perlu tahu apa yang mereka lakukan (kebanyakan memikirkan orang lain yang menggunakan kode Anda di sini).

J_mie6
sumber
1
Saya tidak mengerti. Kecuali jika pemain memperpanjang kelas secara dinamis saat permainan sedang berjalan, bagaimana ini bisa digunakan untuk menipu? Ini menyiratkan bahwa Anda mengimpor kode yang tidak tepercaya ke basis kode Anda. Jika Anda mengatakan bahwa Anda memberi siswa kelas yang dikompilasi dan mencegah mereka menyontek dalam tugas mereka dengan mengubah detail implementasi, menetapkan level perlindungan tidak akan membuat tugas lebih aman. Siswa dapat mendekompilasi perpustakaan atau menggunakan refleksi untuk keuntungan mereka. Ini tentu saja tergantung pada tingkat penegakan yang Anda kejar.
Sam
@sam Umumnya kode harus ditulis dengan asumsi bahwa siapa pun yang mengubahnya selanjutnya tidak perlu mengetahui seluruh basis kode dalam ke luar. Itu bahkan bisa menjadi penulis asli di waktu (waktu berlalu, kurang tidur ...). Kecurangan bisa jadi siswa diberi kode kerangka untuk menyempurnakan, dan mengubah logika yang seharusnya tidak disentuh.
Phil Lello
0

Metode / variabel pribadi umumnya akan disembunyikan dari subkelas. Itu bisa menjadi hal yang baik.

Metode pribadi dapat membuat asumsi tentang parameter dan membiarkan kewarasan memeriksa ke pemanggil.

Metode yang dilindungi harus memeriksa input kewarasan.

Phil Lello
sumber
-1

"Pribadi" berarti: Tidak dimaksudkan untuk diubah atau diakses oleh siapa pun kecuali kelas itu sendiri. Tidak dimaksudkan untuk diubah atau diakses oleh subclass. Subkelas? Subkelas apa? Anda tidak seharusnya mensubklasifikasikan ini!

"protected" berarti: Hanya dimaksudkan untuk diubah atau diakses oleh kelas atau subclass. Mungkin deduksi bahwa Anda seharusnya subclass, kalau tidak mengapa "dilindungi" dan bukan "pribadi"?

Ada perbedaan yang jelas di sini. Jika saya membuat sesuatu pribadi, Anda harus menjaga jari-jari Anda yang kotor. Bahkan jika Anda adalah subkelas.

gnasher729
sumber
-1

Salah satu hal pertama yang saya lakukan ketika saya subkelas kelas adalah mengubah banyak metode pribadi untuk dilindungi

Beberapa penalaran tentang privatevs protected metode :

privatemetode mencegah penggunaan kembali kode. Subkelas tidak dapat menggunakan kode dalam metode pribadi dan mungkin harus mengimplementasikannya lagi - atau menerapkan kembali metode yang awalnya bergantung pada metode pribadi & c.

Di sisi lain, metode apa pun yang tidak private dapat dilihat sebagai API yang disediakan oleh kelas untuk "dunia luar", dalam arti bahwa subkelas pihak ketiga juga dianggap "dunia luar", seperti yang disarankan orang lain dalam jawabannya. sudah.

Itu adalah hal yang buruk? - Kurasa tidak.

Tentu saja, API publik (pseudo-) mengunci programmer asli dan menghalangi refactoring dari antarmuka tersebut. Tetapi jika dilihat sebaliknya, mengapa seorang programmer tidak perlu mendesain sendiri "detail implementasi" dengan cara yang bersih dan stabil seperti API publiknya? Haruskah dia menggunakan privatesehingga dia bisa ceroboh tentang penataan kode "pribadi" nya? Berpikir mungkin dia bisa membersihkannya nanti, karena tidak ada yang akan memperhatikan? - Tidak.

Programmer harus memasukkan sedikit pemikiran ke dalam kode "privat" -nya juga, untuk menyusunnya sedemikian rupa sehingga memungkinkan atau bahkan mempromosikan penggunaan ulang sebanyak mungkin di tempat pertama. Maka bagian-bagian non-pribadi mungkin tidak menjadi beban di masa depan seperti beberapa ketakutan.

Banyak (framework) kode saya melihat mengadopsi penggunaan konsisten dari private: protected, metode non-final yang hampir tidak melakukan apa-apa lebih dari mendelegasikan kepada metode pribadi umumnya ditemukan. protected, metode non-final yang kontraknya hanya dapat dipenuhi melalui akses langsung ke bidang pribadi juga.

Metode-metode ini tidak dapat secara logis diganti / ditingkatkan, meskipun secara teknis tidak ada yang membuat (compiler-) jelas.

Ingin dapat diperpanjang dan diwariskan? Jangan membuat metode Anda private.

Tidak ingin perilaku kelas Anda diubah? Buat metode Anda final.

Benarkah metode Anda tidak bisa disebut di luar konteks tertentu yang terdefinisi dengan baik? Jadikan metode Anda privatedan / atau pikirkan tentang bagaimana Anda dapat membuat konteks yang ditentukan dengan baik tersedia untuk digunakan kembali melalui protectedmetode pembungkus lain .

Itu sebabnya saya menganjurkan untuk menggunakan privatehemat. Dan tidak membingungkan privatedengan final. - Jika implementasi metode sangat penting untuk kontrak umum kelas dan karenanya tidak boleh diganti / diganti, buatlah final!

Untuk bidang, privatetidak terlalu buruk. Selama bidang tersebut dapat "digunakan" secara wajar melalui metode yang sesuai (itu tidak getXX() atau setXX()!).

JimmyB
sumber
2
-1 Ini tidak membahas pertanyaan asli privatevs protected, memberikan saran yang salah ("gunakan privatehemat"?) Dan bertentangan dengan konsensus umum tentang desain OO. Menggunakan privatetidak ada hubungannya dengan menyembunyikan kode ceroboh.
Andres F.
3
Anda menyebutkan bahwa suatu kelas memiliki API, tetapi tampaknya tidak membuat koneksi yang tidak ingin dibebani oleh pengguna API dengan perincian yang tidak perlu mereka ketahui. Situasi ideal bagi pengguna kelas adalah antarmuka hanya berisi metode yang mereka butuhkan, dan tidak ada yang lain. Membuat metode privatemembantu menjaga API tetap bersih.
Ben Cottrell
1
@HannoBinder Saya setuju bahwa ada banyak kelas di luar sana yang mengalami kekakuan, namun fleksibilitas dan penggunaan kembali kode jauh lebih tidak penting daripada enkapsulasi dan pemeliharaan jangka panjang. Pertimbangkan kasus Anda di mana semua kelas memiliki anggota yang dilindungi dan kelas turunan dapat dipusingkan dengan detail implementasi yang mereka inginkan; bagaimana Anda andal menguji unit-unit tersebut dengan mengetahui bahwa apa pun di bagian mana pun dari kode yang belum tertulis dapat menyebabkan kode itu gagal kapan saja? berapa banyak "peretasan" yang bisa diambil kode sebelum semuanya berubah menjadi bola lumpur yang besar?
Ben Cottrell
2
Oh, kamu bilang "anggota". Apa yang saya katakan hanya merujuk pada metode . Variabel anggota (bidang) adalah cerita yang berbeda, di mana privasi jauh lebih dibenarkan.
JimmyB
2
Nomenklatur dalam diskusi ada di mana-mana karena tidak spesifik untuk bahasa tertentu. "Anggota" di C ++ dapat berupa data, fungsi, tipe, atau templat.
JDługosz
-1

Apakah Anda tahu tentang kasus penggunaan penting di mana privat alih-alih dilindungi adalah alat yang baik, atau apakah dua opsi "terproteksi & publik" sudah cukup untuk bahasa OOP?

Pribadi : ketika Anda memiliki sesuatu yang tidak akan pernah berguna untuk memanggil atau mengganti subclass.

Dilindungi : ketika Anda memiliki sesuatu yang memiliki implementasi / konstanta khusus subkelas.

Sebuah contoh:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}
Arnab Datta
sumber
2
ketika Anda memiliki sesuatu yang tidak akan pernah berguna bagi subclass mana pun untuk dipanggil atau ditimpa - Dan ini adalah sesuatu yang menurut Anda, tidak pernah Anda ketahui sebelumnya.
Adam Libuša
Yah, saya mungkin juga perlu menekan tombol reset pada CMOS saya. Tetapi asumsi standarnya adalah bahwa saya tidak akan benar-benar membutuhkannya, dan karena itu diletakkan di dalam sasis. Hal yang sama berlaku untuk metode. Anda membuatnya terlindungi jika subclass benar-benar perlu mengimplementasikan / menyebutnya (lihat: membunyikan klakson). Anda menjadikannya publik jika pihak lain perlu menyebutnya.
Arnab Datta
-2

Sulit bagi saya untuk memahami masalah ini, jadi saya ingin berbagi pengalaman saya:

  • Apa bidang yang dilindungi ? Ini tidak lebih dari sebuah lapangan, yang tidak dapat diakses di luar kelas, yaitu publik seperti ini: $classInstance->field. Dan triknya adalah "ini dia". Anak-anak kelas Anda akan memiliki akses penuh ke sana, karena itu adalah bagian internal yang sah.
  • Apa bidang pribadi ? Ini adalah " benar pribadi " untuk kelas Anda sendiri dan pelaksanaan Anda sendiri dari kelas ini. "Jauhkan dari jangkauan anak-anak", seperti pada botol obat. Anda akan memiliki jaminan bahwa itu tidak dapat dilanggar oleh turunan kelas Anda, metode Anda - ketika dipanggil - akan memiliki persis apa yang telah Anda nyatakan

PEMBARUAN: contoh praktis dari tugas nyata yang telah saya pecahkan. Ini dia: Anda punya token, seperti USB atau LPT (itu kasus saya), dan Anda punya middleware. Token meminta Anda kode pin, terbuka jika itu benar dan Anda dapat mengirim bagian terenkripsi dan sejumlah kunci untuk menguraikan. Kunci disimpan dalam token, Anda tidak dapat membacanya, hanya menggunakannya. Dan ada kunci sementara untuk sesi, ditandatangani oleh kunci dalam token, tetapi disimpan dalam middleware itu sendiri. Kunci temp tidak seharusnya bocor ke mana saja di luar, hanya ada pada level pengemudi. Dan saya menggunakan bidang pribadi untuk menyimpan kunci sementara ini dan beberapa data yang terkait dengan perangkat keras. Jadi tidak ada turunan yang dapat menggunakan tidak hanya antarmuka publik, tetapi juga beberapa yang dilindungisubrutin "berguna" yang saya buat untuk tugas, tetapi tidak dapat membuka kotak kuat dengan kunci dan interaksi HW. Masuk akal?

Alexey Vesnin
sumber
maaf kawan! baru saja mengacaukan mereka - memperbarui jawaban saya =) TERIMA KASIH! Saya sedang terburu-buru dan salah ketik =)
Alexey Vesnin
2
Saya pikir OP menyadari perbedaan antara kedua tingkat perlindungan, tetapi tertarik mengapa satu harus digunakan di atas yang lain.
Sam
@ Sam, silakan membaca jawaban saya lebih dalam. Mereka adalah jenis bidang yang sama sekali berbeda ! satu - satunya kesamaan yang mereka miliki adalah bahwa mereka berdua tidak dapat dirujuk secara publik
Alexey Vesnin
2
Saya tidak yakin apa yang Anda maksud. Saya menyadari perbedaan antara pribadi dan dilindungi. Mungkin Anda salah mengerti apa yang saya maksud dengan tingkat perlindungan? Saya mengacu pada 'tingkat akses. Saya mencoba menunjukkan bahwa walaupun jawaban Anda tidak buruk, jawaban itu tidak langsung menjawab pertanyaan. OP tampaknya mengetahui apa yang keduanya dilindungi / privat / publik, tetapi tidak yakin dalam keadaan apa mereka ingin memilih satu dari yang lain. Ini mungkin cara Anda ditolak (bukan oleh saya).
Sam
@ Sam Saya pikir saya mengerti maksud Anda - menambahkan kasus praktis dari praktik / pengalaman nyata saya. Apakah itu yang Anda maksudkan?
Alexey Vesnin