Pribadi vs Terlindungi - Kepedulian Praktik Baik Visibilitas [ditutup]

145

Saya telah mencari dan saya tahu perbedaan teoretisnya.

  • publik - Setiap kelas / fungsi dapat mengakses metode / properti.
  • protected - Hanya kelas ini dan semua subclass yang dapat mengakses metode / properti.
  • pribadi - Hanya kelas ini yang dapat mengakses metode / properti. Bahkan tidak akan diwarisi.

Semua baik-baik saja dan baik, pertanyaannya adalah, apa perbedaan praktis di antara mereka? Kapan Anda akan menggunakan privatedan kapan Anda akan menggunakan protected? Apakah ada standar atau praktik yang baik yang dapat diterima untuk hal ini?

Sampai sekarang, untuk mempertahankan konsep pewarisan dan polimorfisme, saya menggunakan publicapa pun yang harus diakses dari luar (seperti konstruktor dan fungsionalitas kelas utama), dan protecteduntuk metode internal (logika, metode pembantu dll). Apakah saya di jalur yang benar?

(Perhatikan bahwa pertanyaan ini untuk saya, tetapi juga untuk referensi di masa depan karena saya belum melihat pertanyaan seperti ini SO).

Hantu Madara
sumber
33
Apakah itu penting? Bahasa apa pun dengan dukungan OOP memiliki masalah ini. Saya kebetulan memprogram dalam PHP, tapi saya pikir pertanyaannya berlaku untuk bahasa pendukung OOP.
Hantu Madara
2
Oke cukup adil, hanya ingin tahu apakah Anda lupa memberi tag. Sekarang saya melihat tag oop.
Paul Bellora
Saya harus mengatur visibilitas (pribadi / publik / terlindungi) untuk setiap properti kelas? Atau hanya beberapa dari mereka yang perlu memiliki jenis visibilitas? Jika ya, bagaimana cara memutuskan properti mana yang harus memiliki visibilitas yang ditetapkan di atas kelas?
user4271704
1
begitu saja, perbedaan antara dilindungi dan pribadi tampak jelas. Gunakan dilindungi jika subclass akan menggunakan metode / variabel, jika tidak gunakan pribadi. Khususnya, jika subclass harus mendefinisikan ulang variabel pribadi yang sangat mirip di induknya, buat saja itu dilindungi.
iPherian
Ada specifier akses lain internaldalam bahasa C # yang membatasi tingkat akses kelas atau anggotanya dalam majelis fisik. Padahal, saya tidak yakin tentang dukungannya atau sesuatu yang serupa dalam bahasa lain.
RBT

Jawaban:

128

Tidak, Anda tidak berada di jalur yang benar. Aturan praktis yang baik adalah: jadikan segala sesuatu senyaman mungkin. Ini membuat kelas Anda lebih dienkapsulasi, dan memungkinkan untuk mengubah internal kelas tanpa mempengaruhi kode menggunakan kelas Anda.

Jika Anda mendesain kelas Anda agar dapat diwariskan, maka pilihlah dengan hati-hati apa yang mungkin ditimpa dan dapat diakses dari subkelas, dan buat yang terlindungi (dan final, berbicara tentang Jawa, jika Anda ingin membuatnya dapat diakses tetapi tidak dapat ditimpa). Tetapi perlu diketahui bahwa, segera setelah Anda menerima untuk memiliki subclass dari kelas Anda, dan ada bidang atau metode yang dilindungi, bidang atau metode ini adalah bagian dari API publik dari kelas tersebut, dan mungkin tidak dapat diubah kemudian tanpa melanggar subclass.

Kelas yang tidak dimaksudkan untuk diwariskan harus dibuat final (di Jawa). Anda mungkin mengendurkan beberapa aturan akses (privat ke terlindungi, final ke non-final) demi pengujian unit, tetapi kemudian mendokumentasikannya, dan memperjelas bahwa meskipun metode ini dilindungi, itu tidak seharusnya diganti.

JB Nizet
sumber
1
Pertanyaan sebenarnya di sini adalah tentang privatevs protectedKapan saya ingin properti diwarisi, dan kapan tidak? Saya benar-benar tidak tahu apakah pengguna kadang-kadang di masa depan ingin mengambil kelas saya dan memperpanjangnya ...
Madara's Ghost
16
Nah, pertanyaannya bukan apa yang ingin ditimpa pengguna , pertanyaannya adalah apa yang ingin Anda biarkan ditimpa. Biasanya akan membantu untuk beralih sisi dan mencoba berpikir: Jika saya menggunakan kelas itu dan membuat subclass, apa yang ingin saya timpa? Terkadang pengguna lain masih akan melewatkan hal-hal ...
Thorsten Dittmar
3
Bidang yang dilindungi adalah praktik buruk dalam pewarisan! . Berhati-hatilah. Inilah sebabnya
RBT
4
Bidang pribadi dalam praktiknya buruk dalam warisan !. (berlebihan) Baca jawaban saya.
Larry
6
Opini kontrol-aneh yang khas.
bruno desthuilliers
58

Saya perkenalkan ini dengan mengatakan saya berbicara terutama tentang akses metode di sini, dan pada tingkat yang sedikit lebih rendah, menandai kelas final, bukan akses anggota.

Kebijaksanaan lama

"Tandai sebagai pribadi kecuali Anda memiliki alasan kuat untuk tidak

masuk akal di hari-hari ketika ditulis, sebelum open source mendominasi ruang perpustakaan pengembang dan VCS / dependensi mgmt. menjadi sangat kolaboratif berkat Github, Maven, dll. Saat itu ada juga uang yang dihasilkan dengan membatasi cara di mana sebuah perpustakaan dapat digunakan. Saya mungkin menghabiskan 8 atau 9 tahun pertama karir saya secara ketat mengikuti "praktik terbaik" ini.

Hari ini, saya percaya itu saran yang buruk. Kadang-kadang ada argumen yang masuk akal untuk menandai metode pribadi, atau final kelas tetapi sangat jarang, dan bahkan mungkin tidak memperbaiki apa pun.

Pernahkah kamu:

  • Pernah kecewa, terkejut atau terluka oleh perpustakaan dll. Yang memiliki bug yang bisa diperbaiki dengan pewarisan dan beberapa baris kode, tetapi karena metode pribadi / final dan kelas terpaksa menunggu patch resmi yang mungkin tidak pernah datang? Saya sudah.
  • Ingin menggunakan perpustakaan untuk kasus penggunaan yang sedikit berbeda dari yang dibayangkan oleh penulis tetapi tidak dapat melakukannya karena metode dan kelas pribadi / akhir? Saya sudah.
  • Pernah kecewa, terkejut atau terluka oleh perpustakaan dll. Yang terlalu permisif dalam diperpanjang itu? Aku belum.

Ini adalah tiga rasionalisasi terbesar yang saya dengar untuk menandai metode pribadi secara default:

Rasionalisasi # 1: Tidak aman dan tidak ada alasan untuk mengganti metode tertentu

Saya tidak dapat menghitung berapa kali saya salah tentang apakah akan ada kebutuhan untuk mengganti metode tertentu yang saya tulis. Setelah bekerja pada beberapa lib open source populer, saya belajar dengan cara yang sulit biaya sebenarnya menandai hal-hal pribadi. Ini sering menghilangkan satu-satunya solusi praktis untuk masalah yang tidak terduga atau kasus penggunaan. Sebaliknya, saya belum pernah dalam 16+ tahun pengembangan profesional menyesal menandai metode yang dilindungi dan bukan pribadi karena alasan yang terkait dengan keselamatan API. Ketika seorang pengembang memilih untuk memperluas kelas dan mengganti metode, mereka secara sadar mengatakan "Saya tahu apa yang saya lakukan." dan demi produktivitas yang seharusnya cukup. Titik. Jika berbahaya, catat di Javadocs kelas / metode, jangan membanting pintu begitu saja.

Metode penandaan yang dilindungi secara default adalah mitigasi untuk salah satu masalah utama dalam pengembangan SW modern: kegagalan imajinasi.

Rasionalisasi # 2: Itu menjaga API publik / Javadocs bersih

Yang satu ini lebih masuk akal, dan tergantung pada audiens target, itu mungkin bahkan merupakan hal yang tepat untuk dilakukan, tetapi ada baiknya mempertimbangkan berapa biaya untuk menjaga API "bersih" sebenarnya adalah: ekstensibilitas. Untuk alasan yang disebutkan di atas, mungkin lebih masuk akal untuk menandai hal-hal yang dilindungi secara default untuk berjaga-jaga.

Rasionalisasi # 3: Perangkat lunak saya bersifat komersial dan saya perlu membatasi penggunaannya.

Ini masuk akal juga, tetapi sebagai konsumen saya akan pergi dengan pesaing yang kurang ketat (dengan asumsi tidak ada perbedaan kualitas yang signifikan) setiap saat.

Jangan pernah bilang tidak akan pernah

Saya tidak mengatakan tidak pernah menandai metode pribadi. Saya mengatakan aturan praktis yang lebih baik adalah "membuat metode terlindungi kecuali ada alasan bagus untuk tidak".

Saran ini paling cocok untuk mereka yang bekerja di perpustakaan atau proyek skala besar yang telah dipecah menjadi modul. Untuk proyek yang lebih kecil atau lebih monolitik, itu cenderung tidak terlalu berarti karena Anda tetap mengendalikan semua kode dan mudah untuk mengubah tingkat akses kode Anda jika / ketika Anda membutuhkannya. Meski begitu, saya masih akan memberikan saran yang sama :-)

Nick
sumber
Wrt your Rationalization # 1- Ketika saya menandai sesuatu sebagai terlindungi saya memilih untuk melakukannya secara eksplisit karena saya merasa bahwa perilaku ini tidak final dan mungkin merupakan kasus di mana kelas anak saya ingin menimpanya. Jika saya tidak begitu yakin maka saya ingin menjadikannya pribadi secara default. Khususnya dalam bahasa seperti C # yang membuat metode non-virtual secara default, menambahkan specifier akses yang dilindungi tidak masuk akal. Anda harus menambahkan keduanya virtualdan protectedkata kunci untuk membuat maksud Anda sangat jelas. Juga, orang lebih suka asosiasi daripada warisan sehingga protectedstandar sulit untuk dipahami
RBT
4
Kombinasi kata kunci yang diperlukan untuk membuat metode dapat ditimpa adalah detail bahasa IMHO. Argumen balasan yang saya sajikan untuk rasionalisasi # 1 ditujukan untuk kasus yang Anda sebutkan "Jika saya tidak begitu yakin ...". Secara praktis, jika Anda tidak dapat memikirkan alasan mengapa itu akan berbahaya maka ada lebih banyak yang bisa diperoleh dengan memilih untuk diperpanjang. Ketika Anda mengatakan 'asosiasi', saya menganggapnya sebagai sinonim untuk komposisi, dalam hal ini saya tidak menganggapnya penting.
Nick
3
Saya tidak ingat berapa kali saya harus "mengkloning" kelas yang mendasarinya hanya karena saya ingin menimpa 1 atau 2 atau metode. Dan seperti yang Anda katakan, dengan tren sosial bahwa kami membagikan lebih banyak kode daripada sebelumnya, masuk akal jika menggunakan perlindungan daripada pribadi secara default. yaitu menjadi lebih terbuka untuk logika Anda sendiri.
shinkou
1
Setuju. Saya hanya ingin mengubah Java BufferedReader untuk menangani pembatas baris kustom. Berkat bidang pribadi saya harus mengkloning seluruh kelas daripada hanya memperpanjang dan menimpa readLine ().
Ron Dunn
21

Berhenti menyalahgunakan bidang pribadi !!!

Komentar di sini tampaknya sangat mendukung penggunaan bidang pribadi. Nah, kalau begitu saya punya sesuatu yang berbeda untuk dikatakan.

Apakah pada dasarnya bidang pribadi baik? Iya. Tetapi mengatakan bahwa aturan emas adalah menjadikan segalanya pribadi ketika Anda tidak yakin pasti salah ! Anda tidak akan melihat masalah sampai Anda menemukan satu. Menurut pendapat saya, Anda harus menandai bidang sebagai terlindungi jika Anda tidak yakin .

Ada dua kasus yang Anda ingin perpanjang kelas:

  • Anda ingin menambahkan fungsionalitas tambahan ke kelas dasar
  • Anda ingin memodifikasi kelas yang ada di luar paket saat ini (di beberapa perpustakaan mungkin)

Tidak ada yang salah dengan bidang pribadi dalam kasus pertama. Fakta bahwa orang-orang menyalahgunakan bidang pribadi membuatnya sangat frustasi ketika Anda tahu Anda tidak dapat memodifikasi apa pun.

Pertimbangkan perpustakaan sederhana yang memodelkan mobil:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Penulis perpustakaan berpikir: tidak ada alasan pengguna perpustakaan saya perlu mengakses detail implementasi yang assembleCar()benar? Mari kita tandai sekrup sebagai pribadi.

Ya, penulis salah. Jika Anda hanya ingin memodifikasi assembleCar()metode tanpa menyalin seluruh kelas ke dalam paket Anda, Anda kurang beruntung. Anda harus menulis ulang screwbidang Anda sendiri . Katakanlah mobil ini menggunakan selusin sekrup, dan masing-masingnya melibatkan beberapa kode inisialisasi tidakrivial dalam metode pribadi yang berbeda, dan sekrup ini semuanya ditandai pribadi. Pada titik ini, ia mulai menyedot.

Ya, Anda dapat berdebat dengan saya bahwa penulis perpustakaan dapat menulis kode yang lebih baik sehingga tidak ada yang salah dengan bidang pribadi . Saya tidak berpendapat bahwa bidang pribadi adalah masalah OOP . Ini adalah masalah ketika orang menggunakannya.

Moral dari cerita ini adalah, jika Anda menulis perpustakaan, Anda tidak pernah tahu apakah pengguna Anda ingin mengakses bidang tertentu. Jika Anda tidak yakin, tandai protectedsehingga semua orang akan lebih bahagia nanti. Setidaknya jangan menyalahgunakan bidang pribadi .

Saya sangat mendukung jawaban Nick.

Larry
sumber
2
Menggunakan bidang pribadi sebagian besar mengatakan bahwa warisan adalah abstraksi yang salah untuk mengubah perilaku tertentu dari suatu kelas / objek (jika digunakan secara sadar). Mengubah biasanya akan secara diam-diam melanggar LSP karena perilaku diubah tanpa perubahan API. Jawaban bagus, +1.
Madara's Ghost
Berkhotbah! Pribadi adalah kutukan keberadaan saya ketika mencoba menulis mod Minecraft. Tidak ada yang lebih menyebalkan daripada harus menggunakan Reflection atau ASM untuk memperbaikinya.
RecursiveExceptionException
Ketika saya mengerti jawaban Nick, dia merujuk sebagian besar pada metode, bukan properti. Saya sepenuhnya setuju bahwa kita tidak boleh mendeklarasikan metode pribadi setiap kali dan penggunaan kebutuhan itu perlu dipertimbangkan, tetapi mengapa Anda menyarankan untuk mendeklarasikan properti yang dilindungi privasi hanya karena Anda ingin lebih mudah "menulis ulang" kelas. Sepenuhnya berbagi rasa frustrasi Anda karena saya bekerja dengan harian ke-3, tetapi mari kita mendorong orang untuk membuat arsitektur yang solid dan tidak hanya mengatakan "kode akan berakhir menjadi omong kosong jadi mari kita lindungi dari awal sehingga kita dapat dengan mudah menulis ulang itu". Meskipun saya berbagi rasa frustrasi Anda.
sean662
16

Saya membaca sebuah artikel beberapa waktu lalu yang berbicara tentang mengunci setiap kelas sebanyak mungkin. Jadikan semuanya final dan pribadi kecuali Anda memiliki kebutuhan mendesak untuk mengekspos beberapa data atau fungsionalitas ke dunia luar. Selalu mudah untuk memperluas ruang lingkup agar lebih diizinkan di kemudian hari, tetapi tidak sebaliknya. Pertama-tama pertimbangkan membuat sebanyak mungkin hal finalyang akan membuat memilih privatedan protectedlebih mudah.

  1. Jadikan semua kelas final kecuali jika Anda perlu subklas segera.
  2. Buat semua metode menjadi final kecuali Anda perlu membuat subkelas dan menimpanya segera.
  3. Buat semua parameter metode final kecuali Anda perlu mengubahnya di dalam tubuh metode, yang agak canggung sebagian besar kali.

Sekarang jika Anda pergi dengan kelas terakhir, maka buat semuanya pribadi kecuali ada sesuatu yang benar-benar dibutuhkan oleh dunia - buat itu publik.

Jika Anda ditinggalkan dengan kelas yang memiliki subclass (es), maka hati-hati memeriksa setiap properti dan metode. Pertama-tama pertimbangkan apakah Anda bahkan ingin mengekspos properti / metode itu ke subkelas. Jika Anda melakukannya, maka pertimbangkan apakah subclass dapat mendatangkan malapetaka pada objek Anda jika itu mengacaukan nilai properti atau implementasi metode dalam proses pengesampingan. Jika memungkinkan, dan Anda ingin melindungi properti / metode kelas Anda bahkan dari subclass (terdengar ironis, saya tahu), maka jadikan itu pribadi. Kalau tidak membuatnya terlindungi.

Penafian: Saya tidak banyak memprogram di Jawa :)

Anurag
sumber
2
Untuk memperjelas: A final classdi Java adalah kelas yang tidak dapat diwarisi. A final methodadalah non-virtual, yaitu tidak dapat ditimpa oleh subclass (metode Java adalah virtual secara default). A final field/parameter/variableadalah referensi variabel yang tidak dapat diubah (dalam arti bahwa itu tidak dapat dimodifikasi setelah diinisialisasi).
M.Stramm
1
Lucunya, saya telah melihat saran yang berlawanan persis, bahwa tidak ada yang harus final kecuali pasti bahwa mengesampingkan hal tersebut memiliki potensi untuk memecahkan invarian. Dengan begitu siapa pun bebas memperpanjang kelas Anda sesuai kebutuhan.
GordonM
Bisakah Anda menyebutkan satu kasus dalam karir pemrograman Anda di mana menandai parameter metode "final" membuat perbedaan bagi pemahaman Anda? (selain jika diminta oleh kompiler)
user949300
7

Kapan Anda akan menggunakan privatedan kapan Anda akan menggunakan protected?

Warisan Pribadi dapat dianggap Diterapkan dalam hal hubungan daripada hubungan IS-A . Sederhananya, antarmuka eksternal dari kelas yang mewarisi tidak memiliki hubungan (terlihat) dengan kelas yang diwarisi, Ia menggunakan privatewarisan hanya untuk mengimplementasikan fungsi yang serupa yang disediakan oleh kelas Base.

Tidak seperti, Private Warisan, Warisan terlindungi adalah bentuk terbatas dari Warisan, di mana kelas turunan IS-A semacam kelas Base dan ia ingin membatasi akses anggota yang diturunkan hanya ke kelas turunan.

Alok Simpan
sumber
2
Bahwa "Diimplementasikan dalam hubungan" adalah hubungan HAS-A. Jika semua atribut bersifat pribadi, satu-satunya cara untuk mengaksesnya adalah melalui metode. Ini adalah hal yang sama seperti jika sub-kelas berisi objek kelas-super. Menghilangkan semua atribut yang dilindungi dari kelas konkret Anda berarti menghilangkan semua warisan dari mereka juga.
shawnhcorey
2
Proses pemikiran yang menarik - Warisan Pribadi . @shawnhcorey terima kasih telah membuatnya jelas bahwa ini menunjuk ke hubungan HAS-A alias asosiasi (yang dapat berupa agregasi atau komposisi). Saya merasa posting ini juga harus diperbarui untuk mengklarifikasi hal yang sama.
RBT
1

Yah itu semua tentang enkapsulasi jika kelas tagihan menangani penagihan pembayaran maka di kelas produk mengapa perlu seluruh proses proses penagihan yaitu metode pembayaran cara membayar di mana harus membayar .. jadi hanya membiarkan apa yang digunakan untuk kelas dan objek lain tidak lebih dari publik untuk mereka yang menggunakan kelas lain juga, terlindungi hanya untuk kelas yang diperluas. Seperti Anda madara uchiha pribadi seperti "limboo"Anda dapat melihatnya (Anda hanya kelas satu kelas).

ujwal dhakal
sumber