Enkapsulasi memberitahu saya untuk membuat semua atau hampir semua bidang menjadi pribadi dan mengeksposnya dengan getter / setter. Tapi sekarang perpustakaan seperti Lombok muncul yang memungkinkan kita untuk mengekspos semua bidang pribadi dengan satu penjelasan singkat @Data
. Ini akan membuat getter, setter dan pengaturan konstruktor untuk semua bidang pribadi.
Bisakah seseorang menjelaskan kepada saya apa arti menyembunyikan semua bidang sebagai pribadi dan setelah itu mengekspos semuanya dengan teknologi ekstra? Mengapa kita tidak hanya menggunakan bidang publik saja? Saya merasa kami telah berjalan jauh dan sulit hanya untuk kembali ke titik awal.
Ya, ada teknologi lain yang bekerja melalui getter dan setter. Dan kita tidak dapat menggunakannya melalui bidang publik sederhana. Tetapi teknologi ini muncul hanya karena kami memiliki banyak properti - bidang pribadi di belakang pengambil / penetap publik. Jika kita tidak memiliki properti, teknologi ini akan mengembangkan cara lain dan akan mendukung bidang publik. Dan semuanya akan sederhana dan kita tidak akan membutuhkan Lombok sekarang.
Apa arti dari keseluruhan siklus ini? Dan apakah enkapsulasi benar - benar masuk akal sekarang dalam pemrograman kehidupan nyata?
sumber
"It will create getters, setters and setting constructors for all private fields."
- Cara Anda menjelaskan alat ini, kedengarannya seperti itu adalah mempertahankan enkapsulasi. (Setidaknya dalam pengertian model yang longgar, otomatis, dan agak anemia.) Jadi, apa sebenarnya masalahnya?Jawaban:
Jika Anda mengekspos semua atribut Anda dengan getter / setter Anda mendapatkan hanya struktur data yang masih digunakan dalam C atau bahasa prosedural lainnya. Ini bukan enkapsulasi dan Lombok hanya membuat untuk bekerja dengan kode prosedural kurang menyakitkan. Getters / setter seburuk bidang publik biasa. Tidak ada perbedaan sebenarnya.
Dan struktur data bukan objek. Jika Anda akan mulai membuat objek dari menulis antarmuka, Anda tidak akan pernah menambahkan getter / setter ke antarmuka. Mengekspos atribut Anda mengarah ke kode prosedural spaghetti di mana manipulasi dengan data berada di luar objek dan menyebar ke seluruh basis kode. Sekarang Anda berurusan dengan data dan manipulasi dengan data alih-alih berbicara dengan objek. Dengan getter / setters, Anda akan memiliki pemrograman prosedural berbasis data di mana manipulasi dengan yang dilakukan dengan cara imperatif langsung. Dapatkan data - lakukan sesuatu - setel data.
Dalam OOP enkapsulasi adalah gajah jika dilakukan dengan cara yang benar. Anda harus merangkum rincian implementasi dan negara sehingga objek memiliki kontrol penuh untuk itu. Logika akan difokuskan di dalam objek dan tidak akan tersebar di seluruh basis kode. Dan ya - enkapsulasi masih penting dalam pemrograman karena kode akan lebih dapat dipertahankan.
EDIT
Setelah melihat diskusi yang terjadi, saya ingin menambahkan beberapa hal:
Jadi jika kita kembali ke pertanyaan mengapa kita harus melakukan ini. Pertimbangkan contoh sederhana ini:
Di sini kita memiliki Dokumen yang mengekspos detail internal oleh pengambil dan memiliki kode prosedural eksternal dalam
printDocument
fungsi yang bekerja dengan yang di luar objek. Mengapa ini buruk? Karena sekarang Anda hanya memiliki kode gaya C. Ya itu terstruktur tapi apa bedanya? Anda dapat menyusun fungsi C dalam file dan nama yang berbeda. Dan mereka yang disebut layer melakukan hal itu. Kelas layanan hanyalah sekelompok prosedur yang bekerja dengan data. Kode itu kurang dapat dipelihara dan memiliki banyak kelemahan.Bandingkan dengan yang ini. Sekarang kami memiliki kontrak dan rincian implemantasi dari kontrak ini disembunyikan di dalam objek. Sekarang Anda dapat benar-benar menguji kelas itu dan kelas itu merangkum beberapa data. Cara kerjanya dengan data itu adalah objek perhatian. Untuk berbicara dengan objek sekarang, Anda perlu memintanya untuk mencetak sendiri. Itu enkapsulasi dan itu adalah sebuah objek. Anda akan mendapatkan kekuatan penuh injeksi ketergantungan, mengejek, pengujian, tanggung jawab tunggal dan banyak manfaat dengan OOP.
sumber
Maksudnya adalah Anda tidak seharusnya melakukan itu .
Enkapsulasi berarti Anda hanya mengekspos bidang-bidang yang benar-benar Anda butuhkan untuk diakses kelas lain, dan Anda sangat selektif dan berhati-hati.
Jangan tidak hanya memberikan semua bidang getter dan setter per default!
Itu benar-benar bertentangan dengan semangat spesifikasi JavaBeans yang ironisnya darimana konsep pengambil dan pendatang publik berasal.
Tetapi jika Anda melihat speknya, Anda akan melihat bahwa itu dimaksudkan untuk membuat getter dan setter sangat selektif, dan itu berbicara tentang properti "read-only" (no setter) dan "write-only" properti (no pengambil).
Faktor lain adalah bahwa getter dan setter belum tentu akses mudah ke bidang pribadi . Seorang pengambil dapat menghitung nilai yang dikembalikannya dengan cara yang rumit, mungkin menyimpannya. Setter dapat memvalidasi nilai atau memberi tahu pendengar.
Jadi itulah Anda: enkapsulasi berarti Anda hanya mengekspos fungsionalitas yang sebenarnya perlu Anda paparkan. Tetapi jika Anda tidak berpikir tentang apa yang perlu Anda ungkapkan dan mau tidak mau memaparkan semuanya dengan mengikuti beberapa transformasi sintaksis maka tentu saja itu tidak benar-benar enkapsulasi.
sumber
getFieldName()
menjadi kontrak otomatis bagi kami. Kami tidak akan mengharapkan beberapa perilaku rumit di balik itu. Dalam kebanyakan kasus itu hanya pemecahan langsung enkapsulasi.Saya pikir inti masalahnya dijelaskan oleh komentar Anda:
Masalah yang Anda miliki adalah bahwa Anda mencampur datamodel kegigihan dengan datamodel aktif .
Suatu aplikasi umumnya akan memiliki beberapa datamodels:
di atas datamodel yang sebenarnya digunakan untuk melakukan komputasinya.
Secara umum, datamodels yang digunakan untuk komunikasi dengan luar harus diisolasi dan independen dari datamodel dalam (model objek bisnis, BOM) di mana perhitungan dilakukan:
Dalam skenario ini, sangat baik untuk objek yang digunakan dalam lapisan komunikasi untuk memiliki semua item publik atau diekspos oleh getter / setter. Itu adalah benda biasa tanpa invarian apa pun.
Di sisi lain, BOM Anda harus memiliki invarian, yang umumnya menghalangi memiliki banyak setter (getter tidak mempengaruhi invarian, meskipun mereka mengurangi enkapsulasi ke tingkat tertentu).
sumber
Pertimbangkan yang berikut ini ..
Anda memiliki
User
kelas dengan propertiint age
:Anda ingin meningkatkan ini sehingga
User
memiliki tanggal lahir, berlawanan dengan usia. Menggunakan pengambil:Kami dapat menukar
int age
bidang dengan yang lebih kompleksLocalDate dateOfBirth
:Tidak ada pelanggaran kontrak, tidak ada kerusakan kode .. Tidak lebih dari meningkatkan perwakilan internal dalam persiapan untuk perilaku yang lebih kompleks.
Bidang itu sendiri dienkapsulasi.
Sekarang, untuk menghapus kekhawatiran ..
@Data
Anotasi Lombok mirip dengan kelas data Kotlin .Tidak semua kelas mewakili objek perilaku. Adapun melanggar enkapsulasi, itu tergantung pada penggunaan fitur ini oleh Anda. Anda seharusnya tidak mengekspos semua bidang Anda melalui getter.
Dalam arti yang lebih umum, enkapsulasi adalah tindakan menyembunyikan informasi. Jika Anda menyalahgunakan
@Data
, maka mudah untuk menganggap Anda mungkin melanggar enkapsulasi. Tetapi itu tidak berarti tidak memiliki tujuan. JavaBeans, misalnya, disukai oleh beberapa orang. Namun itu digunakan secara luas dalam pengembangan usaha.Akankah Anda menyimpulkan bahwa pengembangan usaha itu buruk, karena penggunaan kacang? Tentu saja tidak! Persyaratan berbeda dari pengembangan standar. Bisakah kacang disalahgunakan? Tentu saja! Mereka disalahgunakan sepanjang waktu!
Lombok juga mendukung
@Getter
dan@Setter
mandiri - gunakan apa yang diminta oleh persyaratan Anda.sumber
@Data
anotasi pada suatu jenis, yang dengan desain tidak merangkum semua bidangsetAge(xyz)
Itu bukan bagaimana enkapsulasi didefinisikan dalam pemrograman berorientasi objek. Enkapsulasi berarti bahwa setiap objek harus seperti kapsul, yang kulit luarnya (api publik) melindungi dan mengatur akses ke interiornya (metode dan bidang pribadi), dan menyembunyikannya dari pandangan. Dengan menyembunyikan internal, penelepon tidak bergantung pada internal, memungkinkan internal diubah tanpa mengubah (atau bahkan mengkompilasi ulang) penelepon. Juga, enkapsulasi memungkinkan setiap objek untuk menegakkan invariannya sendiri, dengan hanya membuat operasi yang aman tersedia untuk penelepon.
Oleh karena itu enkapsulasi adalah kasus khusus dari penyembunyian informasi, di mana setiap objek menyembunyikan internalnya dan menegakkan invariannya.
Menghasilkan getter dan setter untuk semua bidang adalah bentuk enkapsulasi yang cukup lemah, karena struktur data internal tidak disembunyikan, dan invarian tidak dapat ditegakkan. Itu memang memiliki keuntungan bahwa Anda dapat mengubah cara data disimpan secara internal (selama Anda dapat mengkonversi ke dan dari struktur lama) tanpa harus mengubah (atau bahkan mengkompilasi ulang) penelepon, namun.
Sebagian, ini karena kecelakaan bersejarah. Strike satu adalah bahwa, di Jawa, ekspresi pemanggilan metode dan ekspresi akses bidang secara sintaksis berbeda di situs panggilan, yaitu mengganti akses bidang dengan pengambil atau penyetel memecah API kelas. Karenanya, jika Anda mungkin memerlukan pengakses, Anda harus menulisnya sekarang, atau dapat memecahkan API. Tidak adanya dukungan properti tingkat bahasa ini sangat kontras dengan bahasa modern lainnya, terutama C # dan EcmaScript .
Strike 2 adalah bahwa spesifikasi JavaBeans mendefinisikan properti sebagai getter / setter, bidang bukan properti. Akibatnya, sebagian besar kerangka kerja perusahaan awal mendukung pengambil / penentu, tetapi bukan bidang. Itu sudah lama di masa lalu sekarang ( Java Persistence API (JPA) , Validasi Bean , Arsitektur Java untuk XML Binding (JAXB) , Jacksonsemua bidang dukungan baik-baik saja sekarang), tetapi tutorial dan buku-buku lama terus berlama-lama, dan tidak semua orang sadar bahwa semuanya telah berubah. Tidak adanya dukungan properti tingkat bahasa masih bisa menjadi masalah dalam beberapa kasus (misalnya karena pemuatan malas JPA entitas tunggal tidak memicu ketika bidang publik dibaca), tetapi sebagian besar bidang publik berfungsi dengan baik. Intinya, perusahaan saya menulis semua DTO untuk API REST mereka dengan bidang publik (bagaimanapun, tidak mendapatkan lebih banyak publik yang ditransmisikan melalui internet, setelah semua :-).
Yang mengatakan, Lombok
@Data
tidak lebih dari menghasilkan getter / setter: Ini juga menghasilkantoString()
,hashCode()
danequals(Object)
, yang bisa sangat berharga.Enkapsulasi bisa sangat berharga atau sama sekali tidak berguna, tergantung pada objek yang dienkapsulasi. Secara umum, semakin kompleks logika di dalam kelas, semakin besar manfaat enkapsulasi.
Getter dan setter yang dihasilkan secara otomatis untuk setiap bidang umumnya digunakan secara berlebihan, tetapi dapat berguna untuk bekerja dengan kerangka kerja lama, atau untuk menggunakan fitur kerangka kerja sesekali yang tidak didukung untuk bidang.
Enkapsulasi dapat dicapai dengan getter dan metode perintah. Setter biasanya tidak sesuai, karena mereka diharapkan hanya mengubah satu bidang, sementara mempertahankan invarian mungkin memerlukan beberapa bidang untuk diubah sekaligus.
Ringkasan
getter / setter menawarkan enkapsulasi yang agak buruk.
Prevalensi getter / pemukim di Jawa berasal dari kurangnya dukungan tingkat bahasa untuk properti, dan pilihan desain yang dipertanyakan dalam model komponen historisnya yang telah diabadikan dalam banyak bahan pengajaran dan programmer yang diajarkan oleh mereka.
Bahasa berorientasi objek lainnya, seperti EcmaScript, melakukan properti dukungan di tingkat bahasa, sehingga getter dapat diperkenalkan tanpa melanggar API. Dalam bahasa seperti itu, getter dapat diperkenalkan ketika Anda benar-benar membutuhkannya, daripada sebelumnya, hanya dalam kasus-Anda-mungkin-membutuhkan-itu-satu-hari, yang membuat pengalaman pemrograman yang jauh lebih menyenangkan.
sumber
Saya sudah bertanya pada diri sendiri pertanyaan itu.
Tapi itu tidak sepenuhnya benar. Prevalensi pengambil / penyetel IMO disebabkan oleh spesifikasi Java Bean, yang mengharuskannya; jadi itu cukup banyak fitur bukan pemrograman Berorientasi Objek tetapi pemrograman berorientasi Bean, jika Anda mau. Perbedaan antara keduanya adalah lapisan abstraksi yang ada di dalamnya; Kacang lebih merupakan antarmuka sistem, yaitu pada lapisan yang lebih tinggi. Mereka abstrak dari landasan OO, atau setidaknya dimaksudkan - seperti biasa, hal-hal yang didorong terlalu jauh terlalu sering.
Saya akan mengatakan itu agak disayangkan bahwa hal Bean yang ada di mana-mana dalam pemrograman Java tidak disertai dengan penambahan fitur bahasa Jawa yang sesuai - Saya sedang memikirkan sesuatu seperti konsep Properties di C #. Bagi mereka yang tidak menyadarinya, ini adalah konstruksi bahasa yang terlihat seperti ini:
Bagaimanapun, seluk beluk implementasi aktual masih sangat banyak manfaat dari enkapsulasi.
sumber
Jawaban sederhana di sini adalah: Anda memang benar. Getters dan setter menghilangkan sebagian besar (tetapi tidak semua) nilai enkapsulasi. Ini bukan untuk mengatakan bahwa setiap kali Anda memiliki metode get dan / atau set yang Anda hancurkan enkapsulasi tetapi jika Anda secara buta menambahkan accessors ke semua anggota pribadi kelas Anda, Anda melakukan kesalahan.
Getters adalah setter yang ada di mana-mana dalam pemrograman Java karena konsep JavaBean didorong sebagai cara untuk secara dinamis mengikat fungsionalitas ke kode pra-dibangun. Misalnya, Anda dapat memiliki formulir dalam applet (ada yang ingat itu?) Yang akan memeriksa objek Anda, menemukan semua properti dan menampilkan bidang sebagai. Kemudian UI dapat memodifikasi properti tersebut berdasarkan input pengguna. Anda sebagai pengembang maka hanya khawatir tentang menulis kelas dan meletakkan validasi atau logika bisnis di sana dll.
Menggunakan Kacang Contoh
Ini bukan ide yang buruk, tetapi saya belum pernah menjadi penggemar berat pendekatan di Jawa. Itu hanya bertentangan dengan keinginan. Gunakan Python, Groovy dll. Sesuatu yang mendukung pendekatan semacam ini lebih alami.
Hal JavaBean semacam berputar di luar kendali karena ia menciptakan JOBOL yaitu pengembang yang menulis Java yang tidak mengerti OO. Pada dasarnya, objek menjadi tidak lebih dari sekumpulan data dan semua logika ditulis di luar dengan metode panjang. Karena dianggap normal, orang-orang seperti Anda dan saya yang mempertanyakan ini dianggap kook. Akhir-akhir ini saya melihat perubahan dan ini bukan posisi orang luar
Hal yang mengikat XML adalah kacang yang sulit. Ini mungkin bukan medan perang yang baik untuk berdiri melawan JavaBeans. Jika Anda harus membuat JavaBeans ini, coba jauhkan dari kode asli. Perlakukan mereka sebagai bagian dari lapisan serialisasi.
sumber
[Serializable]
atribut dan kerabatnya. Mengapa menulis 101 metode serialisasi spesifik / kelas ketika Anda bisa menulis satu dengan meningkatkan refleksi?Berapa banyak yang bisa kita lakukan tanpa getter? Apakah mungkin untuk menghapusnya sepenuhnya? Masalah apa yang terjadi? Bisakah kita bahkan mencekal
return
kata kunci?Ternyata Anda bisa melakukan banyak hal jika Anda bersedia melakukan pekerjaan itu. Lalu bagaimana informasi bisa keluar dari objek yang sepenuhnya dienkapsulasi ini? Melalui kolaborator.
Daripada membiarkan kode mengajukan pertanyaan, Anda mengatakan sesuatu untuk melakukan sesuatu. Jika barang-barang itu juga tidak mengembalikan barang-barang Anda tidak perlu melakukan apa-apa tentang apa yang mereka kembalikan. Jadi ketika Anda berpikir untuk meraih
return
bukan mencoba untuk mencapai beberapa kolaborator port output yang akan melakukan apa pun yang tersisa untuk dilakukan.Melakukan hal-hal seperti ini memiliki manfaat dan konsekuensi. Anda harus memikirkan lebih dari sekadar apa yang akan Anda kembalikan. Anda harus memikirkan bagaimana Anda akan mengirimkannya sebagai pesan ke objek yang tidak memintanya. Mungkin Anda membagikan objek yang sama dengan yang Anda kembalikan, atau mungkin hanya memanggil metode saja sudah cukup. Melakukan hal itu membutuhkan biaya.
Manfaatnya adalah sekarang Anda melakukan pembicaraan dengan antarmuka. Ini berarti Anda mendapatkan manfaat penuh dari abstraksi.
Ini juga memberi Anda pengiriman polimorfik, karena saat Anda tahu apa yang Anda katakan, Anda tidak harus tahu persis apa yang Anda katakan.
Anda mungkin berpikir ini berarti Anda harus pergi hanya satu cara melalui setumpuk lapisan, tetapi ternyata Anda dapat menggunakan polimorfisme untuk mundur tanpa membuat ketergantungan siklik yang gila.
Mungkin terlihat seperti ini:
Jika Anda dapat kode seperti itu maka menggunakan getter adalah pilihan. Bukan kejahatan yang perlu.
sumber
Ini adalah masalah yang kontroversial (seperti yang Anda lihat) karena banyak dogma dan kesalahpahaman bercampur dengan keprihatinan yang masuk akal mengenai masalah getter dan setter. Namun singkatnya, tidak ada yang salah dengan
@Data
itu dan tidak merusak enkapsulasi.Mengapa menggunakan getter dan setter daripada bidang publik?
Karena getter / setter menyediakan enkapsulasi. Jika Anda mengekspos nilai sebagai bidang publik dan kemudian berubah untuk menghitung nilai dengan cepat, maka Anda perlu memodifikasi semua klien yang mengakses bidang tersebut. Jelas ini buruk. Ini adalah detail implementasi apakah beberapa properti dari suatu objek disimpan dalam suatu bidang, dihasilkan dengan cepat atau diambil dari tempat lain, sehingga perbedaannya tidak boleh diekspos kepada klien. Setter pengambil / setter menyelesaikan ini, karena menyembunyikan implementasi.
Tetapi jika pengambil / penyetel hanya mencerminkan bidang pribadi yang mendasarinya, bukankah itu sama buruknya?
Tidak! Intinya adalah enkapsulasi memungkinkan Anda untuk mengubah implementasi tanpa mempengaruhi klien. Bidang mungkin masih merupakan cara yang sangat baik untuk menyimpan nilai, selama klien tidak harus tahu atau peduli.
Tetapi bukankah autogenerating getter / setter dari bidang melanggar enkapsulasi?
Tidak ada enkapsulasi yang masih ada!
@Data
anotasi hanyalah cara mudah untuk menulis pasangan pengambil / penyetel yang menggunakan bidang yang mendasarinya. Untuk sudut pandang klien, ini seperti sepasang pengambil / penyetel biasa. Jika Anda memutuskan untuk menulis ulang implementasi Anda masih bisa melakukannya tanpa mempengaruhi klien. Jadi Anda mendapatkan yang terbaik dari kedua dunia: enkapsulasi dan sintaksis ringkas.Tetapi beberapa mengatakan rajin / rajin selalu buruk!
Ada kontroversi terpisah, di mana beberapa percaya bahwa pola pengambil / penyetel selalu buruk, terlepas dari implementasi yang mendasarinya. Idenya adalah bahwa Anda tidak harus menetapkan atau mendapatkan nilai dari objek, melainkan interaksi antara objek harus dimodelkan sebagai pesan di mana satu objek meminta objek lain untuk melakukan sesuatu. Ini sebagian besar adalah sepotong dogma dari hari-hari awal pemikiran berorientasi objek. Pemikiran sekarang adalah bahwa untuk pola tertentu (misalnya objek nilai, objek transfer data) getter / setter mungkin sangat tepat.
sumber
Enkapsulasi memang memiliki tujuan, tetapi juga dapat disalahgunakan atau disalahgunakan.
Pertimbangkan sesuatu seperti Android API yang memiliki kelas dengan lusinan (jika tidak ratusan) bidang. Mengekspos bidang-bidang yang konsumen API membuatnya sulit untuk dinavigasi dan menggunakan, juga memberikan pengguna gagasan palsu bahwa ia dapat melakukan apa pun yang ia inginkan dengan bidang-bidang yang mungkin bertentangan dengan bagaimana mereka seharusnya digunakan. Jadi enkapsulasi sangat bagus untuk pemeliharaan, kegunaan, keterbacaan, dan menghindari bug gila.
Di sisi lain, POD atau tipe data lama biasa, seperti struct dari C / C ++ di mana semua bidang bersifat publik dapat berguna juga. Memiliki getter / setter yang tidak berguna seperti yang dihasilkan oleh anotasi @ data di Lombok hanyalah cara untuk menjaga "pola enkapsulasi". Salah satu dari sedikit alasan kami melakukan pengambil / setter "tidak berguna" di Jawa adalah bahwa metode tersebut memberikan kontrak .
Di Jawa, Anda tidak bisa memiliki bidang dalam antarmuka, oleh karena itu Anda menggunakan getter dan setter untuk menentukan properti umum yang dimiliki semua pelaksana antarmuka itu. Dalam bahasa yang lebih baru seperti Kotlin atau C # kita melihat konsep properti sebagai bidang yang dapat Anda deklarasikan sebagai setter dan pengambil. Pada akhirnya, getter / setters yang tidak berguna lebih merupakan warisan yang harus dijalani oleh Java, kecuali Oracle menambahkan properti padanya. Kotlin, misalnya, yang merupakan bahasa JVM lain yang dikembangkan oleh JetBrains, memiliki kelas data yang pada dasarnya melakukan apa yang dilakukan oleh anotasi @ data di Lombok.
Juga ada beberapa contoh:
Ini adalah kasus enkapsulasi yang buruk. Para pengambil dan penyetel secara efektif tidak berguna. Enkapsulasi banyak digunakan karena ini adalah standar dalam bahasa seperti Java. Tidak benar-benar membantu, selain menjaga konsistensi di seluruh basis kode.
Ini adalah contoh enkapsulasi yang bagus. Enkapsulasi digunakan untuk menegakkan kontrak, dalam hal ini IDataInterface. Tujuan enkapsulasi dalam contoh ini adalah untuk membuat konsumen kelas ini menggunakan metode yang disediakan oleh antarmuka. Meskipun pengambil dan penyetel tidak melakukan apa pun yang mewah, kami sekarang telah mendefinisikan sifat umum antara DataClass dan pelaksana lainnya dari IDataInterface. Dengan demikian saya dapat memiliki metode seperti ini:
Sekarang, ketika berbicara tentang enkapsulasi saya pikir penting untuk juga mengatasi masalah sintaksis. Saya sering melihat orang mengeluh tentang sintaksis yang diperlukan untuk menegakkan enkapsulasi daripada enkapsulasi itu sendiri. Salah satu contoh yang muncul dalam pikiran adalah dari Casey Muratori (Anda dapat melihat kata-katanya di sini ).
Misalkan Anda memiliki kelas pemain yang menggunakan enkapsulasi dan ingin memindahkan posisinya dengan 1 unit. Kode akan terlihat seperti ini:
Tanpa enkapsulasi akan terlihat seperti ini:
Di sini ia berpendapat bahwa enkapsulasi mengarah ke lebih banyak mengetik tanpa manfaat tambahan dan ini dalam banyak kasus bisa benar, tetapi perhatikan sesuatu. Argumen itu menentang sintaksis, bukan enkapsulasi itu sendiri. Bahkan dalam bahasa seperti C yang tidak memiliki konsep enkapsulasi, Anda akan sering melihat variabel dalam struct yang dipraxif atau sufixed dengan '_' atau 'my' atau apa pun untuk menandakan bahwa mereka tidak boleh digunakan oleh pengguna API, seolah-olah mereka adalah pribadi.
Faktanya adalah enkapsulasi dapat membantu membuat kode lebih mudah dirawat dan mudah digunakan. Pertimbangkan kelas ini:
Jika variabel bersifat publik dalam contoh ini, maka konsumen API ini akan bingung kapan harus menggunakan posX dan posY dan kapan harus menggunakan setPosition (). Dengan menyembunyikan detail itu, Anda membantu konsumen lebih baik menggunakan API Anda dengan cara yang intuitif.
Sintaksnya adalah batasan dalam banyak bahasa. Namun bahasa yang lebih baru menawarkan properti yang memberi kita sintaksis yang bagus dari anggota publice dan manfaat enkapsulasi. Anda akan menemukan properti di C #, Kotlin, bahkan di C ++ jika Anda menggunakan MSVC. di sini adalah contoh di Kotlin.
kelas VerticalList: ... {var posX: Int set (x) {field = x; ...} var posY: Int set (y) {field = y; ...}}
Di sini kita mencapai hal yang sama seperti pada contoh Java, tetapi kita dapat menggunakan posX dan posY seolah-olah mereka variabel publik. Ketika saya mencoba mengubah nilainya, isi set settter () akan dieksekusi.
Di Kotlin misalnya, ini akan menjadi setara dengan Java Bean dengan getter, setter, kode hash, sama dengan dan toString diimplementasikan:
Perhatikan bagaimana sintaks ini memungkinkan kita melakukan Java Bean dalam satu baris. Anda dengan benar memperhatikan masalah yang dimiliki bahasa seperti Java dalam mengimplementasikan enkapsulasi, tetapi itu adalah kesalahan Java bukan enkapsulasi itu sendiri.
Anda mengatakan bahwa Anda menggunakan @Data Lombok untuk menghasilkan getter dan setter. Perhatikan namanya, @ Data. Ini sebagian besar dimaksudkan untuk digunakan pada kelas data yang hanya menyimpan data dan dimaksudkan untuk serial dan deserialized. Pikirkan sesuatu seperti menyimpan file dari gim. Tetapi dalam skenario lain, seperti dengan elemen UI, Anda paling pasti menginginkan setter karena hanya mengubah nilai variabel mungkin tidak cukup untuk mendapatkan perilaku yang diharapkan.
sumber
Enkapsulasi memberi Anda fleksibilitas . Dengan memisahkan struktur dan antarmuka, ini memungkinkan Anda untuk mengubah struktur tanpa mengubah antarmuka.
Misalnya jika Anda menemukan bahwa Anda perlu menghitung properti berdasarkan bidang lain alih-alih menginisialisasi bidang yang mendasari konstruksi, Anda cukup mengubah pengambil. Jika Anda telah mengekspos bidang secara langsung, Anda harus mengubah antarmuka dan membuat perubahan di setiap situs penggunaan.
sumber
Saya akan mencoba mengilustrasikan ruang masalah enkapsulasi dan desain kelas, dan menjawab pertanyaan Anda di akhir.
Seperti disebutkan dalam jawaban lain, tujuan enkapsulasi adalah untuk menyembunyikan detail internal suatu objek di balik API publik, yang berfungsi sebagai kontrak. Objek aman untuk mengubah internalnya karena tahu itu hanya dipanggil melalui API publik.
Apakah masuk akal untuk memiliki bidang publik, atau getter / setter, atau metode transaksi tingkat tinggi atau pesan yang lewat tergantung pada sifat domain yang sedang dimodelkan. Dalam buku Akka Concurrency (yang dapat saya rekomendasikan meskipun agak ketinggalan zaman) Anda menemukan contoh yang menggambarkan hal ini, yang akan saya ringkas di sini.
Pertimbangkan kelas Pengguna:
Ini berfungsi baik dalam konteks single-threaded. Domain yang dimodelkan adalah nama seseorang, dan mekanisme bagaimana nama itu disimpan dapat dienkapsulasi dengan sempurna oleh para setter.
Namun, bayangkan ini harus disediakan dalam konteks multi-utas. Misalkan satu utas secara berkala membaca nama:
Dan dua utas lainnya berjuang tarik ulur, mengaturnya untuk Hillary Clinton dan Donald Trump pada gilirannya. Mereka masing-masing perlu memanggil dua metode. Sebagian besar ini berfungsi dengan baik, tetapi sesekali Anda akan melihat Hillary Trump atau Donald Clinton lewat.
Anda tidak dapat menyelesaikan masalah ini dengan menambahkan kunci di dalam setter, karena kunci hanya ditahan selama pengaturan baik nama depan atau nama belakang. Satu-satunya solusi melalui penguncian adalah dengan menambahkan kunci di sekitar seluruh objek, tetapi itu memecah enkapsulasi karena kode panggilan harus mengelola kunci (dan dapat menyebabkan kebuntuan).
Ternyata, tidak ada solusi bersih melalui penguncian. Solusi bersih adalah merangkum ulang internal dengan membuatnya lebih kasar:
Nama itu sendiri telah menjadi tidak berubah, dan Anda melihat bahwa anggotanya dapat menjadi publik, karena sekarang menjadi objek data murni tanpa kemampuan untuk memodifikasinya setelah dibuat. Pada gilirannya, API publik kelas Pengguna menjadi lebih kasar, dengan hanya satu setter tersisa, sehingga namanya hanya dapat diubah secara keseluruhan. Itu merangkum lebih banyak keadaan internal di belakang API.
Apa yang Anda lihat dalam siklus ini adalah upaya untuk menerapkan solusi yang baik untuk keadaan tertentu terlalu luas. Tingkat enkapsulasi yang sesuai membutuhkan pemahaman tentang domain yang dimodelkan dan menerapkan tingkat enkapsulasi yang tepat. Terkadang ini berarti semua bidang bersifat publik, kadang-kadang (seperti dalam aplikasi Akka) itu berarti Anda tidak memiliki API publik sama sekali kecuali metode tunggal untuk menerima pesan. Namun, konsep enkapsulasi itu sendiri, yang berarti menyembunyikan internal di belakang API yang stabil, adalah kunci untuk pemrograman perangkat lunak pada skala, terutama dalam sistem multi-threaded.
sumber
Saya bisa memikirkan satu kasus penggunaan di mana ini masuk akal. Anda mungkin memiliki kelas yang semula Anda akses melalui API pengambil / penyetel sederhana. Anda nanti memperluas atau memodifikasi sehingga tidak lagi menggunakan bidang yang sama, tetapi masih mendukung API yang sama .
Contoh yang agak dibuat-buat: Titik yang dimulai sebagai pasangan Cartesian dengan
p.x()
danp.y()
. Anda nanti membuat implementasi atau subkelas baru yang menggunakan koordinat kutub, jadi Anda juga bisa memanggilp.r()
danp.theta()
, tetapi kode klien Anda yang memanggilp.x()
danp.y()
tetap valid. Kelas itu sendiri secara transparan mengkonversi dari bentuk kutub internal, yaituy()
sekarangreturn r * sin(theta);
. (Dalam contoh ini, pengaturan sajax()
atauy()
tidak masuk akal, tetapi masih memungkinkan.)Dalam hal ini, Anda mungkin mendapati diri Anda berkata, "Senang saya repot-repot secara otomatis mendeklarasikan getter dan setter daripada membuat bidang publik, atau saya harus menghancurkan API saya di sana."
sumber
Sama sekali tidak ada gunanya. Namun, fakta Anda mengajukan pertanyaan itu menunjukkan bahwa Anda belum mengerti apa yang Lombok lakukan, dan bahwa Anda tidak mengerti bagaimana menulis kode OO dengan enkapsulasi. Mari kita mundur sedikit ...
Beberapa data untuk instance kelas akan selalu internal, dan tidak boleh diekspos. Beberapa data untuk instance kelas perlu diatur secara eksternal, dan beberapa data mungkin harus dikeluarkan kembali dari instance kelas. Kami mungkin ingin mengubah bagaimana kelas melakukan hal-hal di bawah permukaan, jadi kami menggunakan fungsi untuk memungkinkan kami mendapatkan dan mengatur data.
Beberapa program ingin menyimpan keadaan untuk instance kelas, jadi ini mungkin memiliki beberapa antarmuka serialisasi. Kami menambahkan lebih banyak fungsi yang memungkinkan instance kelas menyimpan statusnya ke penyimpanan dan mengambil statusnya dari penyimpanan. Ini mempertahankan enkapsulasi karena instance kelas masih mengendalikan datanya sendiri. Kami mungkin membuat serialisasi data pribadi, tetapi program lainnya tidak memiliki akses ke sana (atau lebih tepatnya, kami memelihara Tembok Cina dengan memilih untuk tidak dengan sengaja merusak data pribadi itu), dan instance kelas dapat (dan harus) melakukan pemeriksaan integritas deserialisation untuk memastikan datanya kembali OK.
Terkadang data membutuhkan pemeriksaan jangkauan, pemeriksaan integritas, atau hal-hal seperti itu. Menulis fungsi-fungsi ini sendiri memungkinkan kita melakukan semua itu. Dalam hal ini kami tidak ingin atau membutuhkan Lombok, karena kami melakukan semua itu sendiri.
Namun, sering kali Anda menemukan bahwa parameter yang diatur secara eksternal disimpan dalam variabel tunggal. Dalam hal ini Anda akan membutuhkan empat fungsi untuk mendapatkan / mengatur / membuat serial / deserialise isi dari variabel itu. Menulis keempat fungsi ini sendiri setiap kali memperlambat Anda dan rentan terhadap kesalahan. Mengotomatiskan proses dengan Lombok mempercepat pengembangan Anda dan menghilangkan kemungkinan kesalahan.
Ya, itu mungkin untuk membuat variabel itu publik. Dalam versi kode khusus ini, ini akan identik secara fungsional. Tetapi kembalilah ke mengapa kita menggunakan fungsi: "Kami mungkin ingin mengubah cara kelas melakukan hal-hal di bawah permukaan ..." Jika Anda membuat variabel Anda publik, Anda membatasi kode Anda sekarang dan selamanya untuk menjadikan variabel publik ini sebagai antarmuka. Jika Anda menggunakan fungsi, atau jika Anda menggunakan Lombok untuk secara otomatis menghasilkan fungsi-fungsi itu untuk Anda, Anda bebas untuk mengubah data yang mendasarinya dan implementasi yang mendasarinya setiap saat di masa depan.
Apakah ini membuatnya lebih jelas?
sumber
Saya sebenarnya bukan pengembang Java; tetapi berikut ini cukup banyak platform agnostik.
Hampir semua yang kami tulis menggunakan getter dan setter publik yang mengakses variabel pribadi. Sebagian besar getter dan setter sepele. Tetapi ketika kita memutuskan bahwa setter perlu menghitung ulang sesuatu atau setter melakukan validasi atau kita perlu meneruskan properti ke properti variabel anggota kelas ini, ini sama sekali tidak melanggar seluruh kode dan kompatibel dengan biner sehingga kita dapat menukar satu modul keluar.
Ketika kami memutuskan properti ini benar-benar harus dihitung dengan cepat, semua kode yang melihatnya tidak perlu diubah dan hanya kode yang menulisnya yang perlu diubah dan IDE dapat menemukannya untuk kami. Ketika kami memutuskan bahwa ini adalah bidang komputasi yang dapat ditulisi (hanya pernah harus melakukan itu beberapa kali) kami juga dapat melakukannya. Yang menyenangkan adalah beberapa perubahan ini kompatibel biner (berubah menjadi bidang yang dihitung hanya baca tidak secara teori tetapi mungkin dalam praktiknya tetap).
Kami telah berakhir dengan banyak dan banyak getter sepele dengan setter rumit. Kami juga berakhir dengan beberapa caching getter. Hasil akhirnya adalah Anda diperbolehkan berasumsi bahwa getter cukup murah tetapi seter mungkin tidak. Di sisi lain, kami cukup bijak untuk memutuskan bahwa setingan tidak bertahan ke disk.
Tapi saya harus melacak orang yang secara buta mengubah semua variabel anggota menjadi properti. Dia tidak tahu apa yang ditambahkan dengan atom sehingga dia mengubah hal yang benar-benar perlu menjadi variabel publik ke properti dan memecahkan kode dengan cara yang halus.
sumber
Getters dan setter adalah ukuran "berjaga-jaga" yang ditambahkan demi menghindari refactoring di masa depan jika struktur internal atau persyaratan akses berubah selama proses pengembangan.
Katakanlah, beberapa bulan setelah rilis, klien Anda memberi tahu Anda bahwa salah satu bidang kelas kadang-kadang disetel ke nilai negatif, meskipun itu harus menjepit ke 0 paling banyak dalam kasus itu. Menggunakan bidang publik, Anda harus mencari setiap penugasan bidang ini di seluruh basis kode Anda untuk menerapkan fungsi penjepitan ke nilai yang akan Anda tetapkan, dan perlu diingat bahwa Anda akan selalu harus melakukannya ketika memodifikasi bidang ini, tapi itu menyebalkan. Sebaliknya, jika Anda sudah menggunakan getter dan setter, Anda bisa memodifikasi metode setField () Anda untuk memastikan penjepitan ini selalu diterapkan.
Sekarang, masalah dengan bahasa "kuno" seperti Java adalah bahwa mereka mendorong penggunaan metode untuk tujuan ini, yang hanya membuat kode Anda jauh lebih verbose. Sangat sulit untuk menulis, dan sulit dibaca, itulah sebabnya kami telah menggunakan IDE yang mengurangi masalah ini dengan satu atau lain cara. Sebagian besar IDE akan secara otomatis menghasilkan getter dan setter untuk Anda, dan juga menyembunyikannya kecuali jika diberitahukan sebaliknya. Lombok melangkah lebih jauh dan hanya membuatnya secara prosedural selama waktu kompilasi untuk menjaga kode Anda lebih ramping. Namun, bahasa lain yang lebih modern telah memecahkan masalah ini dengan satu atau lain cara. Bahasa Scala atau .NET, misalnya,
Misalnya, dalam VB .NET atau C #, Anda dapat dengan mudah membuat semua bidang yang dimaksudkan untuk memiliki - tidak ada efek samping - pengatur dan pengaturan hanya bidang publik, dan kemudian menjadikannya pribadi, mengubah nama mereka dan mengekspos Properti dengan nama sebelumnya dari bidang, tempat Anda dapat menyempurnakan perilaku akses bidang, jika Anda memerlukannya. Dengan Lombok, jika Anda perlu menyesuaikan perilaku pengambil atau penyetel, Anda bisa menghapus tag itu saat dibutuhkan, dan memberi kode Anda sendiri dengan persyaratan baru, dengan mengetahui bahwa Anda harus memperbaiki apa pun di file lain.
Pada dasarnya, cara metode Anda mengakses bidang harus transparan dan seragam. Bahasa modern memungkinkan Anda mendefinisikan "metode" dengan sintaks akses / panggilan yang sama pada suatu bidang, sehingga modifikasi ini dapat dilakukan sesuai permintaan tanpa banyak memikirkannya selama pengembangan awal, tetapi Java memaksa Anda untuk melakukan pekerjaan ini sebelumnya karena memang tidak memiliki fitur ini. Semua yang dilakukan Lombok adalah menghemat waktu Anda, karena bahasa yang Anda gunakan tidak ingin Anda menghemat waktu untuk metode "berjaga-jaga".
sumber
foo.bar
tetapi dapat ditangani oleh pemanggilan metode. Anda mengklaim ini lebih unggul daripada cara "kuno" untuk membuat API terlihat seperti pemanggilan metodefoo.getBar()
. Kami tampaknya setuju bahwa bidang publik bermasalah, tetapi saya mengklaim bahwa alternatif "kuno" lebih unggul daripada yang "modern", karena seluruh API kami simetris (itu semua panggilan metode). Dalam pendekatan "modern", kita harus memutuskan hal-hal mana yang harus menjadi properti dan mana yang harus menjadi metode, yang terlalu rumit segala sesuatu (terutama jika kita menggunakan refleksi!).Ya, itu kontradiktif. Saya pertama kali berlari ke properti di Visual Basic. Sampai saat itu, dalam bahasa lain, pengalaman saya, tidak ada pembungkus properti di sekitar bidang. Hanya bidang publik, pribadi, dan terlindungi.
Properti adalah semacam enkapsulasi. Saya memahami properti Visual Basic sebagai cara untuk mengontrol dan memanipulasi output dari satu atau beberapa bidang sambil menyembunyikan bidang eksplisit dan bahkan tipe datanya, misalnya memancarkan tanggal sebagai string dalam format tertentu. Tetapi meskipun demikian seseorang tidak "menyembunyikan status dan mengekspos fungsionalitas" dari perspektif objek yang lebih besar.
Tetapi properti membenarkan diri mereka sendiri karena pengambil dan penentu properti yang terpisah. Mengekspos bidang tanpa properti adalah semuanya atau tidak sama sekali - jika Anda bisa membacanya, Anda bisa mengubahnya. Jadi sekarang orang dapat membenarkan desain kelas lemah dengan setter terlindungi.
Jadi mengapa kita / mereka tidak menggunakan metode aktual? Karena itu Visual Basic (dan VBScript ) (oooh! Aaah!), Pengkodean untuk massa (!), Dan itu semua sangat digemari. Dan dengan demikian idiotokrasi akhirnya mendominasi.
sumber