Kode seperti:
public Thing[] getThings(){
return things;
}
Tidak masuk akal, karena metode akses Anda tidak melakukan apa-apa selain secara langsung mengembalikan struktur data internal. Anda mungkin juga menyatakan Thing[] things
demikian public
. Gagasan di balik metode akses adalah untuk membuat antarmuka yang mengisolasi klien dari perubahan internal dan mencegah mereka dari memanipulasi struktur data aktual kecuali dengan cara diam-diam yang diizinkan oleh antarmuka. Ketika Anda mengetahui ketika semua kode klien Anda rusak, metode akses Anda tidak melakukan itu - itu hanya kode yang terbuang. Saya pikir banyak programmer cenderung menulis kode seperti itu karena mereka belajar di suatu tempat bahwa semuanya perlu dienkapsulasi dengan metode akses - tapi itu untuk alasan yang saya jelaskan. Melakukannya hanya untuk "mengikuti formulir" ketika metode akses tidak melayani tujuan apa pun hanyalah kebisingan.
Saya pasti akan merekomendasikan solusi yang Anda usulkan, yang mencapai beberapa tujuan enkapsulasi yang paling penting: Memberi klien antarmuka yang kuat dan bijaksana yang mengisolasi mereka dari detail implementasi internal kelas Anda, dan tidak memungkinkan mereka menyentuh struktur data internal berharap dengan cara yang Anda memutuskan sesuai - "hukum hak istimewa yang paling tidak diperlukan". Jika Anda melihat kerangka kerja OOP besar yang populer, seperti CLR, STL, VCL, pola yang Anda usulkan tersebar luas, untuk alasan itulah.
Haruskah kamu selalu melakukan itu? Belum tentu. Misalnya, jika Anda memiliki kelas pembantu atau teman yang pada dasarnya merupakan komponen dari kelas pekerja utama Anda dan tidak "menghadap ke depan", itu tidak perlu - ini adalah kerja keras yang akan menambahkan banyak kode yang tidak perlu. Dan dalam hal ini, saya tidak akan menggunakan metode akses sama sekali - tidak masuk akal, seperti yang dijelaskan. Cukup nyatakan struktur data dengan cara yang hanya mencakup kelas utama yang menggunakannya - sebagian besar bahasa mendukung cara melakukan itu - friend
, atau mendeklarasikannya dalam file yang sama dengan kelas pekerja utama, dll.
Satu-satunya downside yang bisa saya lihat dalam proposal Anda adalah bahwa itu lebih sulit untuk dikodekan (dan sekarang Anda harus melakukan pengkodean ulang kelas konsumen Anda - tetapi Anda tetap harus melakukan itu.) Tapi itu bukan kelemahan sebenarnya. - Anda perlu melakukannya dengan benar, dan terkadang itu membutuhkan lebih banyak pekerjaan.
Salah satu hal yang membuat programmer baik adalah mereka tahu kapan kerja ekstra itu layak, dan kapan tidak. Dalam jangka panjang menempatkan ekstra sekarang akan membuahkan hasil dengan dividen besar di masa depan - jika tidak pada proyek ini, maka pada yang lain. Belajarlah kode dengan cara yang benar dan gunakan kepala Anda tentang hal itu, bukan hanya secara robotik mengikuti formulir yang ditentukan.
Perhatikan bahwa koleksi yang lebih kompleks daripada array mungkin memerlukan kelas penutup untuk mengimplementasikan lebih dari tiga metode hanya untuk mengakses struktur data internal.
Jika Anda mengekspos seluruh struktur data melalui kelas yang berisi, IMO Anda harus memikirkan mengapa kelas itu dienkapsulasi sama sekali, jika itu bukan hanya untuk menyediakan antarmuka yang lebih aman - "kelas pembungkus". Anda mengatakan kelas yang mengandung tidak ada untuk tujuan itu - jadi mungkin ada sesuatu yang tidak beres tentang desain Anda. Pertimbangkan untuk memecah kelas-kelas Anda menjadi modul-modul yang lebih bijaksana dan meletakannya.
Kelas harus memiliki satu tujuan yang jelas dan rahasia, dan menyediakan antarmuka untuk mendukung fungsi itu - tidak lebih. Anda mungkin mencoba untuk menggabungkan hal-hal yang bukan milik bersama. Ketika Anda melakukan itu, segala sesuatu akan rusak setiap kali Anda harus menerapkan perubahan. Semakin kecil dan bijaksana kelas Anda, semakin mudah untuk mengubah keadaan: Pikirkan LEGO.
get(index)
,add()
,size()
,remove(index)
, danremove(Object)
. Menggunakan teknik yang diusulkan, kelas yang berisi ArrayList ini harus memiliki lima metode publik hanya untuk mendelegasikan ke koleksi batin. Dan tujuan kelas ini dalam program ini kemungkinan besar tidak merangkum ArrayList ini, tetapi melakukan sesuatu yang lain. ArrayList hanyalah detail. [...]remove
tidak baca saja. Pemahaman saya adalah OP ingin mempublikasikan semuanya - seperti dalam kode asli sebelum perubahan yang diusulkan.public Thing[] getThings(){return things;}
Itulah yang tidak saya sukai.Anda bertanya: Haruskah saya selalu merangkum struktur data internal sepenuhnya?
Jawaban Singkat: Ya, sebagian besar waktu tetapi tidak selalu .
Jawaban Panjang: Saya pikir kelas mengikuti kategori berikut:
Kelas yang merangkum data sederhana. Contoh: titik 2D. Sangat mudah untuk membuat fungsi publik yang menyediakan kemampuan untuk mendapatkan / mengatur koordinat X dan Y tetapi Anda dapat menyembunyikan data internal dengan mudah tanpa terlalu banyak kesulitan. Untuk kelas semacam itu, mengekspos detail struktur data internal tidak perlu dilakukan.
Kelas kontainer yang merangkum koleksi. STL memiliki kelas wadah klasik. Saya mempertimbangkan
std::string
dan distd::wstring
antara mereka juga. Mereka menyediakan antarmuka yang kaya untuk berurusan dengan abstraksi tetapistd::vector
,std::string
, danstd::wstring
juga menyediakan kemampuan untuk mendapatkan akses ke data mentah. Saya tidak akan tergesa-gesa memanggil mereka kelas yang dirancang dengan buruk. Saya tidak tahu pembenaran untuk kelas-kelas ini yang mengekspos data mentah mereka. Namun, saya telah, dalam pekerjaan saya, merasa perlu untuk mengekspos data mentah ketika berhadapan dengan jutaan node mesh dan data pada node mesh tersebut untuk alasan kinerja.Hal penting tentang mengekspos struktur internal kelas adalah bahwa Anda harus berpikir panjang dan keras sebelum memberikannya sinyal hijau. Jika antarmuka internal untuk suatu proyek, itu akan mahal untuk mengubahnya di masa depan tetapi bukan tidak mungkin. Jika antarmuka adalah eksternal untuk proyek (seperti ketika Anda sedang mengembangkan perpustakaan yang akan digunakan oleh pengembang aplikasi lain), mungkin tidak mungkin untuk mengubah antarmuka tanpa kehilangan klien Anda.
Kelas yang sebagian besar fungsional di alam. Contoh:
std::istream
,,std::ostream
iterator dari wadah STL. Benar-benar bodoh untuk mengekspos detail internal kelas-kelas ini.Kelas hibrid. Ini adalah kelas yang merangkum beberapa struktur data tetapi juga menyediakan fungsionalitas algoritmik. Secara pribadi, saya pikir ini adalah hasil dari desain yang dipikirkan dengan buruk. Namun, jika Anda menemukannya, Anda harus memutuskan apakah akan masuk akal untuk mengekspos data internal mereka berdasarkan kasus per kasus.
Kesimpulannya: Satu-satunya waktu saya merasa perlu untuk mengekspos struktur data internal kelas adalah ketika itu menjadi hambatan kinerja.
sumber
Alih-alih mengembalikan data mentah secara langsung, coba sesuatu seperti ini
Jadi, Anda pada dasarnya menyediakan koleksi khusus yang menghadirkan wajah apa pun yang diinginkan dunia. Maka dalam implementasi baru Anda,
Sekarang Anda memiliki enkapsulasi yang tepat, menyembunyikan detail implementasi, dan memberikan kompatibilitas mundur (dengan biaya).
sumber
List
. Metode akses yang hanya mengembalikan anggota data, bahkan dengan para pemain untuk membuat hal-hal yang lebih kuat, tidak benar-benar baik enkapsulasi IMO. Kelas pekerja harus menangani semua itu, bukan klien. "Dumber" klien harus, semakin kuat itu. Selain itu, saya tidak yakin Anda menjawab pertanyaan ...Anda harus menggunakan antarmuka untuk hal-hal itu. Tidak akan membantu dalam kasus Anda, karena array Java tidak mengimplementasikan antarmuka itu, tetapi Anda harus melakukannya mulai sekarang:
Dengan cara itu Anda dapat mengubah
ArrayList
keLinkedList
atau apa pun, dan Anda tidak akan merusak kode apapun karena semua koleksi Java (samping array) yang memiliki (semu?) Akses acak mungkin akan mengimplementasikanList
.Anda juga dapat menggunakan
Collection
, yang menawarkan lebih sedikit metode daripadaList
tetapi dapat mendukung koleksi tanpa akses acak, atauIterable
yang bahkan dapat mendukung stream tetapi tidak menawarkan banyak hal dalam hal metode akses ...sumber
List
sebagai kelas turunan, itu akan lebih masuk akal, tetapi hanya "berharap yang terbaik" bukanlah ide yang baik. "Seorang programmer yang baik terlihat jalan dua arah di jalan satu arah".List
(atauCollection
, atau setidaknyaIterable
). Itulah inti dari antarmuka ini, dan itu memalukan bahwa Java Array tidak mengimplementasikannya, tetapi mereka adalah antarmuka resmi untuk koleksi di Jawa sehingga tidak terlalu jauh untuk menganggap koleksi Java akan mengimplementasikannya - kecuali jika koleksi itu lebih tua daripadaList
, dan dalam hal itu sangat mudah untuk membungkusnya dengan AbstractList .List
ke dalamArrayList
, tetapi ini tidak seperti implementasinya yang 100% terlindungi - Anda selalu dapat menggunakan refleksi untuk mengakses bidang pribadi. Melakukan hal itu disukai, tetapi casting juga disukai (tidak sebanyak itu). Inti enkapsulasi bukanlah mencegah peretasan berbahaya - melainkan untuk mencegah pengguna bergantung pada detail implementasi yang mungkin ingin Anda ubah. MenggunakanList
antarmuka tidak persis seperti itu - pengguna kelas dapat bergantung padaList
antarmuka, bukanArrayList
kelas konkret yang mungkin berubah.Ini cukup umum untuk menyembunyikan struktur data internal Anda dari dunia luar. Kadang-kadang itu berlebihan khususnya di DTO. Saya merekomendasikan ini untuk model domain. Jika diperlukan untuk mengekspos, kembalikan salinan yang tidak dapat diubah. Bersamaan dengan ini saya sarankan membuat antarmuka yang memiliki metode ini seperti mendapatkan, mengatur, menghapus dll.
sumber