Mengapa Java membuat akses paket standar?

26

Saya mengajukan pertanyaan ini karena saya percaya mereka melakukannya untuk alasan yang sangat bagus dan kebanyakan orang tidak menggunakannya dengan benar, baik dari pengalaman saya di industri sejauh ini. Tetapi jika teori saya benar maka saya tidak yakin mengapa mereka memasukkan pengubah akses pribadi ...?

Saya percaya bahwa jika akses standar digunakan dengan benar itu memberikan peningkatan kemampuan uji coba sambil mempertahankan enkapsulasi. Dan itu juga menjadikan pengubah akses pribadi berlebihan.

Pengubah akses default dapat digunakan untuk memberikan pengaruh yang sama dengan menggunakan paket unik untuk metode yang perlu disembunyikan dari seluruh dunia, dan ia melakukan ini tanpa mengurangi testabilitas, seperti paket dalam folder uji, dengan paket yang sama. dapat mengakses semua metode default yang dinyatakan dalam folder sumber.

Saya percaya ini sebabnya Java menggunakan akses paket sebagai 'default'. Tapi saya tidak yakin mengapa mereka juga termasuk akses pribadi, saya yakin ada kasus penggunaan yang valid ...

newlogic
sumber
Relevan mengenai unit pengujian yang sebelumnya dibahas metode pribadi; how-do-you-unit-test-private-methods . Menjawab; Anda tidak boleh
Richard Tingle

Jawaban:

25

Saya kira mereka memiliki ide bagus tentang apa yang akan dilakukan oleh programmer biasa. Dan dengan rata-rata programmer yang saya maksud adalah orang yang tidak benar-benar pandai pemrograman, tetapi tetap mendapatkan pekerjaan karena tidak ada yang cukup bagus dan harganya mahal.

Jika mereka membuat "publik" mengakses yang default, kebanyakan programmer tidak akan pernah repot-repot menggunakan yang lain. Hasilnya akan banyak kode spaghetti di mana-mana. (Karena jika Anda secara teknis diizinkan untuk memanggil sesuatu dari mana saja, mengapa pernah repot-repot merangkum beberapa logika dalam suatu metode?)

Menjadikan "pribadi" akses default hanya akan sedikit lebih baik. Kebanyakan programmer pemula hanya akan menciptakan (dan berbagi di blog mereka) aturan praktis bahwa "Anda perlu menulis 'publik' di mana-mana", dan kemudian mereka akan mengeluh mengapa Jawa begitu buruk sehingga memaksa mereka untuk menulis "publik" di mana-mana. Dan mereka juga akan menghasilkan banyak kode spageti.

Akses paket adalah sesuatu yang memungkinkan pemrogram untuk menggunakan teknik pemrograman ceroboh mereka saat menulis kode dalam satu paket, tetapi kemudian mereka harus mempertimbangkannya ketika membuat lebih banyak paket. Ini adalah kompromi antara praktik bisnis yang baik dan kenyataan buruk. Seperti: tulis beberapa kode spageti, jika Anda bersikeras, tapi tolong tinggalkan kekacauan yang jelek di dalam paket; setidaknya buat beberapa antarmuka yang lebih bagus di antara paket-paket.

Mungkin ada alasan lain juga, tapi saya tidak akan meremehkan yang ini.

Viliam Búr
sumber
14

Akses default tidak membuat pengubah akses pribadi menjadi berlebihan.

Posisi desainer bahasa yang tercermin dalam tutorial resmi - Mengontrol Akses ke Anggota Kelas dan itu cukup jelas (untuk kenyamanan Anda, pernyataan yang relevan dalam kutipan dibuat tebal ):

Kiat Memilih Tingkat Akses:

Jika programmer lain menggunakan kelas Anda, Anda ingin memastikan bahwa kesalahan dari penyalahgunaan tidak dapat terjadi. Tingkat akses dapat membantu Anda melakukan ini.

  • Gunakan tingkat akses paling ketat yang masuk akal untuk anggota tertentu. Gunakan pribadi kecuali Anda punya alasan kuat untuk tidak melakukannya.
  • Hindari bidang publik kecuali konstanta. (Banyak contoh dalam tutorial menggunakan bidang publik. Ini dapat membantu menggambarkan beberapa poin secara ringkas, tetapi tidak disarankan untuk kode produksi.) Bidang publik cenderung menghubungkan Anda ke implementasi tertentu dan membatasi fleksibilitas Anda dalam mengubah kode Anda.

Daya tarik Anda terhadap testabilitas sebagai pembenaran untuk sepenuhnya menjatuhkan pengubah pribadi adalah salah, seperti dibuktikan misalnya dengan jawaban di New to TDD. Haruskah saya menghindari metode pribadi sekarang?

Tentu saja Anda dapat memiliki metode pribadi, dan tentu saja Anda dapat mengujinya.

Entah ada beberapa cara untuk mendapatkan metode swasta untuk menjalankan, dalam hal ini Anda dapat menguji seperti itu, atau ada tidak ada cara untuk mendapatkan swasta untuk menjalankan, dalam hal: mengapa sih yang Anda mencoba untuk menguji itu, hanya hapus saja ...


Posisi desainer bahasa pada tujuan dan penggunaan akses tingkat paket dijelaskan dalam tutorial resmi lain, Membuat dan Menggunakan Paket dan tidak ada kesamaan dengan ide menjatuhkan pengubah pribadi (untuk kenyamanan Anda, pernyataan yang relevan dalam kutipan dibuat tebal ) :

Anda harus menggabungkan kelas-kelas ini dan antarmuka dalam sebuah paket karena beberapa alasan, termasuk yang berikut:

  • Anda dan pemrogram lainnya dapat dengan mudah menentukan bahwa jenis ini terkait ...
  • Anda dapat mengizinkan tipe-tipe di dalam paket untuk memiliki akses tidak terbatas satu sama lain namun masih membatasi akses untuk tipe di luar paket ...

<Kata kasar "Kurasa aku sudah cukup banyak mendengar rengekan. Sepertinya sudah waktunya untuk mengatakan dengan keras dan jelas ...">

Metode pribadi bermanfaat untuk pengujian unit.

Catatan di bawah ini mengasumsikan bahwa Anda terbiasa dengan cakupan kode . Jika tidak, luangkan waktu untuk belajar, karena itu cukup berguna bagi mereka yang tertarik dalam pengujian unit dan pengujian sama sekali.

Baiklah, jadi saya punya metode pribadi dan tes unit, dan analisis cakupan memberi tahu saya bahwa ada celah, metode pribadi saya tidak dicakup oleh tes. Sekarang...

Apa yang saya peroleh dari merahasiakannya

Karena metode bersifat pribadi, satu-satunya cara untuk melanjutkan adalah mempelajari kode untuk mempelajari cara menggunakannya melalui API non-pribadi. Biasanya, penelitian semacam itu mengungkapkan bahwa alasan untuk kesenjangan adalah bahwa skenario penggunaan tertentu tidak ada dalam tes.

    void nonPrivateMethod(boolean condition) {
        if (condition) {
            privateMethod();
        }
        // other code...
    }

    // unit tests don't invoke nonPrivateMethod(true)
    //   => privateMethod isn't covered.

Demi kelengkapan, alasan lain (kurang sering) untuk kesenjangan cakupan ini bisa berupa bug dalam spesifikasi / desain. Saya tidak akan terjun jauh ke sini di sini, untuk menjaga hal-hal sederhana; Cukuplah untuk mengatakan bahwa jika Anda melemahkan batasan akses "hanya untuk membuat metode dapat diuji", Anda akan kehilangan kesempatan untuk mengetahui bahwa bug ini ada sama sekali.

Baik, untuk memperbaiki kesenjangan, saya menambahkan unit test untuk skenario yang hilang, ulangi analisis cakupan dan verifikasi bahwa celah itu hilang. Apa yang saya miliki sekarang? Saya telah mendapatkan sebagai unit test baru untuk penggunaan khusus API non-pribadi.

  1. Tes baru memastikan bahwa perilaku yang diharapkan untuk penggunaan ini tidak akan berubah tanpa pemberitahuan karena jika itu berubah, tes akan gagal.

  2. Pembaca dari luar dapat melihat ke tes ini dan belajar bagaimana seharusnya menggunakan dan berperilaku (di sini, pembaca dari luar mencakup diri saya di masa depan, karena saya cenderung melupakan kode satu atau dua bulan setelah saya selesai dengan itu).

  3. Tes baru toleran terhadap refactoring (apakah saya refactor metode pribadi? Anda bertaruh!) Apa pun yang saya lakukan privateMethod, saya selalu ingin menguji nonPrivateMethod(true). Apa pun yang saya lakukan privateMethod, tidak perlu memodifikasi tes karena metode tidak dipanggil secara langsung.

Tidak buruk? Anda bertaruh.

Apa yang saya lepas dari pelemahan batasan akses

Sekarang bayangkan bahwa alih-alih di atas, saya hanya memperlemah batasan akses. Saya melewatkan studi kode yang menggunakan metode dan melanjutkan langsung dengan tes yang memanggil saya exPrivateMethod. Besar? Tidak!

  1. Apakah saya mendapatkan tes untuk penggunaan khusus API non-pribadi yang disebutkan di atas? Tidak: tidak ada tes untuk nonPrivateMethod(true)sebelumnya, dan tidak ada tes seperti itu sekarang.

  2. Apakah pembaca luar mendapatkan kesempatan untuk lebih memahami penggunaan kelas? Tidak. "- Hei apa tujuan dari metode yang diuji di sini? - Lupakan saja, ini hanya untuk penggunaan internal. - Ups."

  3. Apakah toleran terhadap refactoring? Tidak mungkin: apa pun yang saya ubah exPrivateMethod, kemungkinan akan memecahkan tes. Ganti nama, gabungkan ke beberapa metode lain, ubah argumen dan pengujian hanya akan berhenti mengkompilasi. Sakit kepala? Anda bertaruh!

Kesimpulannya , bertahan dengan metode pribadi memberi saya peningkatan yang berguna, andal dalam unit test. Sebaliknya, melemahnya batasan akses "untuk testabilitas" hanya memberi saya kode tes yang tidak jelas dan sulit dipahami, yang juga beresiko permanen dipatahkan oleh refactoring minor; terus terang apa yang saya dapatkan tampak seperti utang teknis .

</rant>

agas
sumber
7
Saya tidak pernah menemukan argumen untuk menguji metode pribadi itu menarik. Anda merefleksikan kode menjadi metode sepanjang waktu karena alasan yang tidak ada hubungannya dengan API publik dari suatu kelas, dan itu adalah hal yang sangat bagus untuk dapat menguji metode tersebut secara mandiri, tanpa melibatkan kode lain. Itulah alasan Anda refactored, bukan? Untuk mendapatkan fungsionalitas independen. Fungsi itu juga harus dapat diuji secara independen.
Robert Harvey
@RobertHarvey menjawab diperluas dengan kata-kata kasar yang membahas hal ini. "Metode pribadi bermanfaat untuk pengujian unit ..."
Agak
Saya mengerti apa yang Anda katakan tentang metode pribadi dan cakupan kode, tetapi saya tidak benar-benar menganjurkan membuat metode-metode itu terbuka agar Anda dapat mengujinya. Saya menganjurkan memiliki cara untuk menguji mereka secara mandiri meskipun mereka pribadi. Anda masih dapat memiliki tes publik yang menyentuh metode pribadi, jika Anda mau. Dan saya dapat melakukan tes pribadi saya.
Robert Harvey
@RobertHarvey, saya mengerti. Kira itu bermuara pada gaya pengkodean. Pada jumlah metode pribadi yang biasanya saya hasilkan, dan pada tingkat saya refactor ini, memiliki tes untuk mereka hanyalah sebuah kemewahan yang saya tidak mampu. API non-pribadi adalah masalah lain, saya cenderung agak enggan dan lambat dalam memodifikasinya
agas
Mungkin Anda lebih baik dalam menulis metode yang berhasil pertama kali daripada saya. Bagi saya, saya perlu menguji metode untuk memastikan bahwa itu berfungsi terlebih dahulu sebelum saya bisa melanjutkan, dan harus mengujinya secara tidak langsung dengan melepaskan serangkaian metode lain akan menjadi canggung dan canggung.
Robert Harvey
9

Alasan yang paling mungkin adalah: ini ada hubungannya dengan sejarah. Nenek moyang Jawa, Oak, hanya memiliki tiga tingkat akses: pribadi, dilindungi, publik.

Kecuali bahwa private di Oak sama dengan package private di Java. Anda dapat membaca bagian 4.10 dari Spesifikasi Bahasa Oak (penekanan milik saya):

Secara default semua variabel dan metode dalam kelas (termasuk konstruktor) bersifat pribadi. Variabel dan metode pribadi hanya dapat diakses dengan metode yang dideklarasikan di kelas, dan bukan oleh subkelasnya atau kelas lain apa pun ( kecuali untuk kelas dalam paket yang sama ).

Jadi untuk titik Anda, akses pribadi, seperti yang dikenal di Jawa, awalnya tidak ada di sana. Tetapi ketika Anda memiliki lebih dari beberapa kelas dalam satu paket, tidak memiliki privat seperti yang kita tahu sekarang akan mengarah ke mimpi buruk tabrakan nama (misalnya, java.until.concurrent memiliki hampir 60 kelas), yang mungkin mengapa mereka memperkenalkannya.

Namun akses paket default (semula disebut pribadi) semantik tidak berubah antara Oak dan Java.

assylias
sumber
5

Saya percaya bahwa jika akses standar digunakan dengan benar itu memberikan peningkatan kemampuan uji coba sambil mempertahankan enkapsulasi. Dan itu juga menjadikan pengubah akses pribadi berlebihan.

Ada situasi di mana seseorang ingin dua kelas terpisah yang lebih terikat daripada mengekspos segala sesuatu dengan publik. Ini memiliki ide yang mirip dari 'teman' di C ++ di mana kelas lain yang dinyatakan sebagai 'teman' dari orang lain dapat mengakses anggota pribadi mereka.

Jika Anda melihat ke jeroan kelas-kelas seperti BigInteger Anda akan menemukan sejumlah paket perlindungan standar pada bidang dan metode (semua yang segitiga biru dalam daftar). Ini memungkinkan kelas-kelas lain dalam paket java.math untuk memiliki akses yang lebih optimal ke jeroan mereka untuk operasi tertentu (Anda dapat menemukan metode ini disebut dalam BigDecimal - BigDecimal didukung oleh BigInteger dan menyimpan implementasi ulang BigInteger lagi . Bahwa ini adalah tingkat paket bukan t masalah untuk perlindungan karena java.math adalah paket yang disegel dan tidak ada kelas lain yang bisa ditambahkan.

Di sisi lain, ada banyak hal yang memang bersifat pribadi, sebagaimana mestinya. Sebagian besar paket tidak disegel. Tanpa pribadi, rekan kerja Anda dapat memasukkan kelas lain ke dalam paket itu dan mengakses bidang pribadi (tidak lagi) dan menghancurkan enkapsulasi.

Sebagai catatan, pengujian unit bukanlah sesuatu yang dipikirkan kembali ketika cakupan perlindungan sedang dibangun. JUnit (kerangka pengujian XUnit untuk Java) tidak disusun sampai 1997 (salah satu penuturan kisah itu dapat dibaca di http://www.martinfowler.com/bliki/Xunit.html ). Versi Alpha dan Beta dari JDK adalah pada tahun 1995 dan JDK 1.0 pada tahun 1996, meskipun tingkat perlindungan tidak benar-benar dipaku sampai JDK 1.0.2 (sebelum itu Anda bisa memiliki private protectedtingkat perlindungan).

Beberapa orang akan berpendapat bahwa standarnya tidak boleh tingkat paket, tetapi pribadi. Yang lain berpendapat bahwa seharusnya tidak ada perlindungan default tetapi semuanya harus dinyatakan secara eksplisit - beberapa pengikut ini akan menulis kode seperti:

public class Foo {
    /* package */ int bar = 42;
    ...
}

Catat komentar di sana.

Alasan sebenarnya mengapa perlindungan tingkat paket adalah default kemungkinan hilang ke catatan desain Java (saya sudah menggali dan tidak dapat menemukan mengapa artikel, banyak orang yang menjelaskan perbedaannya, tetapi tidak ada yang mengatakan "ini adalah alasannya ").

Jadi saya akan menebak. Dan hanya itu - tebakan.

Pertama-tama, orang masih mencoba mencari tahu desain bahasa. Bahasa sejak itu telah belajar dari kesalahan dan keberhasilan Jawa. Ini bisa didaftar sebagai kesalahan untuk tidak memiliki sesuatu yang perlu didefinisikan untuk semua bidang.

  • Para desainer melihat kebutuhan sesekali untuk hubungan 'teman' dari C ++.
  • Para desainer menginginkan pernyataan eksplisit untuk public, dan privatekarena ini memiliki dampak terbesar pada akses.

Dengan demikian, privat tidak default dan well, semuanya jatuh ke tempatnya. Lingkup default dibiarkan sebagai default. Ini mungkin merupakan ruang lingkup pertama yang dibuat dan untuk kepentingan kompatibilitas dengan kode lama, dibiarkan begitu saja.

Seperti yang saya katakan, ini semua dugaan .


sumber
Ah kalau saja fitur teman bisa diterapkan ke anggota secara individual, jadi Anda bisa mengatakan void someFunction () hanya dapat dilihat oleh kelas X ... yang benar-benar akan memperketat enkapsulasi!
newlogic
Oh, tunggu, Anda bisa melakukan ini di C ++, kami membutuhkan ini di Jawa!
newlogic
2
@ user1037729 itu membuat kopling yang lebih ketat antara dua kelas - kelas dengan metode sekarang perlu tahu dari kelas lain yang adalah teman-temannya. Ini cenderung bertentangan dengan filosofi desain yang disediakan Java. Serangkaian masalah yang dapat dipecahkan dengan teman-teman tetapi tidak tingkat perlindungan paket tidak terlalu besar.
2

Enkapsulasi adalah cara untuk mengatakan "Anda tidak perlu memikirkan hal ini".

                 Access Levels
Modifier    Class   Package  Subclass   World
public        Y        Y        Y        Y
protected     Y        Y        Y        N
no modifier   Y        Y        N        N
private       Y        N        N        N

Menempatkan ini ke dalam istilah nonteknis.

  1. Publik: semua orang harus memikirkannya.
  2. Dilindungi: default dan subclass harus tahu tentang itu.
  3. Secara default, jika Anda mengerjakan paket itu, Anda akan mengetahuinya (itu ada tepat di depan mata Anda) mungkin juga membiarkan Anda memanfaatkannya.
  4. Pribadi, Anda hanya perlu tahu tentang ini jika Anda mengerjakan kelas ini.
jmoreno
sumber
Saya tidak berpikir ada yang namanya kelas privat atau kelas yang dilindungi ..
Koray Tugay
@KorayTugay: lihat docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html . Kelas dalam bersifat pribadi. Baca paragraf terakhir.
jmoreno
0

Saya tidak berpikir mereka fokus pada pengujian, hanya karena pengujian tidak terlalu umum saat itu.

Apa yang saya pikir ingin mereka capai adalah enkapsulasi pada tingkat paket. Kelas dapat, seperti Anda ketahui, memiliki metode dan bidang internal dan hanya memaparkannya melalui anggota publik. Dengan cara yang sama paket dapat memiliki kelas internal, metode, dan bidang, dan hanya memaparkan beberapa di antaranya. Jika Anda berpikir seperti ini, sebuah paket memiliki implementasi internal dalam serangkaian kelas, metode, dan bidang, bersama dengan antarmuka publik di kelas, metode, dan bidang lainnya (mungkin sebagian tumpang tindih).

Coders paling berpengalaman yang saya tahu berpikir seperti ini. Mereka mengambil enkapsulasi dan menerapkannya ke tingkat abstraksi yang lebih tinggi dari kelas: paket, atau komponen jika Anda suka. Masuk akal bagi saya bahwa desainer Jawa sudah matang dalam desain mereka, mengingat seberapa baik Jawa masih bertahan.

Alexander Torstling
sumber
Anda benar bahwa pengujian otomatis tidak terlalu umum di sana. Tapi ini desain yang belum matang.
Tom Hawtin - tackline