Encapsulation adalah praktik membungkus data dan perilaku yang mengelola data tersebut dalam satu unit, lalu menyembunyikan detail internal dari pemanggil luar. Tujuannya bukan sekadar menyembunyikan, melainkan menjamin bahwa setiap perubahan state object melewati jalur yang sudah memvalidasi sehingga invarian class selalu terjaga. Pada chapter ini kita akan menyusun class Tabungan yang menyembunyikan field saldo dan pemilik di balik property publik read-only serta method yang memvalidasi setiap perubahan.
Mengapa Encapsulation Penting #
Bayangkan sebuah class rekening bank yang membiarkan field saldo diakses langsung dari luar. Kode pemanggil dapat menulis rekening.saldo = -1000 tanpa pembatasan apa pun. Nilai negatif tersebut mengandung invarian yang rusak — saldo seharusnya tidak boleh negatif. Saat invarian rusak, kode lain yang mengandalkan invarian tersebut akan gagal dengan cara yang sulit dilacak.
Encapsulation menyelesaikan masalah ini dengan dua langkah:
- Menyembunyikan field internal dengan modifier
private. - Menyediakan method atau property publik yang memvalidasi setiap perubahan.
Dengan pola ini, perubahan state hanya terjadi melalui jalur yang sudah dipikirkan. Invarian tetap terjaga tanpa mempercayai pemanggil untuk memeriksa sendiri.
Pola Class dengan Encapsulation #
Pola umum class yang menerapkan encapsulation terdiri dari tiga lapisan:
| Lapisan | Peran | Contoh |
|---|---|---|
Field private | Penyimpanan internal | private var saldo: Long |
| Property publik read-only | Akses baca aman | val saldoSaatIni: Long get() = saldo |
| Method publik | Perubahan state tervalidasi | fun tarik(jumlah): Boolean |
Field private hanya dapat diakses dari dalam class. Property publik membungkus field dengan read-only getter, sehingga pemanggil dapat membaca tanpa dapat menulis. Method publik menerima argumen, memvalidasi, lalu mengubah field internal bila valid.
Contoh Class Tabungan #
Contoh berikut mendefinisikan class Tabungan yang menerapkan encapsulation penuh. Field saldo dan pemilik bersifat private. Pemanggil hanya dapat membaca melalui property saldoSaatIni dan namaPemilik, serta mengubah melalui method setor, tarik, dan gantiPemilik yang masing-masing memvalidasi argumen.
// encapsulation.kt
// Mendemonstrasikan encapsulation: data private, akses lewat method publik tervalidasi.
class Tabungan(pemilikAwal: String, saldoAwal: Long) {
private var saldo: Long = saldoAwal
private var pemilik: String = pemilikAwal
val namaPemilik: String
get() = pemilik
val saldoSaatIni: Long
get() = saldo
fun setor(jumlah: Long): Boolean {
if (jumlah <= 0) return false
saldo += jumlah
return true
}
fun tarik(jumlah: Long): Boolean {
if (jumlah <= 0 || jumlah > saldo) return false
saldo -= jumlah
return true
}
fun gantiPemilik(pemilikBaru: String): Boolean {
if (pemilikBaru.isBlank()) return false
pemilik = pemilikBaru.trim()
return true
}
}
fun main() {
val t = Tabungan("Budi", 100000)
println("Pemilik: ${t.namaPemilik}, Saldo: ${t.saldoSaatIni}")
val setorOk = t.setor(50000)
println("Setor 50000 berhasil? $setorOk, saldo=${t.saldoSaatIni}")
val setorNegatif = t.setor(-1000)
println("Setor -1000 berhasil? $setorNegatif, saldo=${t.saldoSaatIni}")
val tarikOk = t.tarik(30000)
println("Tarik 30000 berhasil? $tarikOk, saldo=${t.saldoSaatIni}")
val tarikBerlebih = t.tarik(500000)
println("Tarik 500000 berhasil? $tarikBerlebih, saldo=${t.saldoSaatIni}")
val gantiOk = t.gantiPemilik(" Siti ")
println("Ganti pemilik berhasil? $gantiOk, pemilik=${t.namaPemilik}")
val gantiKosong = t.gantiPemilik(" ")
println("Ganti pemilik kosong berhasil? $gantiKosong, pemilik=${t.namaPemilik}")
}Output yang Dihasilkan:
Pemilik: Budi, Saldo: 100000
Setor 50000 berhasil? true, saldo=150000
Setor -1000 berhasil? false, saldo=150000
Tarik 30000 berhasil? true, saldo=120000
Tarik 500000 berhasil? false, saldo=120000
Ganti pemilik berhasil? true, pemilik=Siti
Ganti pemilik kosong berhasil? false, pemilik=SitiMari kita bedah bagaimana encapsulation diterapkan:
private var saldo: Long = saldoAwal: fieldsaldotidak dapat dibaca atau ditulis dari luar class. Invarian “saldo tidak negatif” dapat ditegakkan karena tidak ada jalur langsung untuk mengubahnya.val saldoSaatIni: Long get() = saldo: property publik read-only. Pemanggil dapat membaca saldo saat ini tetapi tidak dapat menugaskan ulang. Penulisant.saldoSaatIni = 1000akan ditolak kompilator.fun setor(jumlah: Long): Boolean: method publik mengubahsaldohanya bilajumlah > 0. Pemanggilant.setor(-1000)mengembalikanfalsedansaldotetap150000. Method ini mengembalikanBooleanagar pemanggil dapat mengetahui apakah perubahan berhasil.fun tarik(jumlah: Long): Boolean: method memvalidasi dua kondisi —jumlah > 0danjumlah <= saldo. Karena validasi terjadi di dalam method, pemanggil tidak dapat melewati pemeriksaan dengan jalur lain.fun gantiPemilik(pemilikBaru: String): Boolean: method memvalidasi bahwa string tidak kosong setelahtrim(). Pemanggilant.gantiPemilik(" ")ditolak karena setelahtrim()menghasilkan string kosong.pemilik = pemilikBaru.trim(): method juga membersihkan whitespace sebelum disimpan. Encapsulation tidak hanya memvalidasi — ia juga membakukan format data yang disimpan sehingga kode lain tidak perlu memangkas ulang.
Property sebagai Alat Encapsulation #
Property Kotlin sangat cocok untuk encapsulation karena getter dapat mengembalikan nilai turunan tanpa mengekspos field asli. Pada contoh di atas, saldoSaatIni adalah property publik yang getter-nya membaca field saldo private. Pemanggil tidak tahu apakah saldoSaatIni adalah field langsung atau hasil komputasi — yang penting, pemanggil tidak dapat menulisnya.
Pola ini bermanfaat saat kita ingin mengubah representasi internal tanpa memengaruhi API publik. Misalnya, bila kelak saldo dipindahkan ke struktur Map<String, Long>, getter saldoSaatIni dapat disesuaikan tanpa mengubah kode pemanggil.
Invarian yang Dijaga Class #
Class Tabungan menjaga dua invarian utama yang dapat dipercaya pemanggil:
saldo >= 0selalu —setormenolak nilai negatif,tarikmenolak penarikan melebihi saldo.pemiliktidak pernah kosong atau hanya whitespace —gantiPemilikmenolak input kosong setelahtrim().
Karena invarian dipegang oleh class, kode pemanggil dapat menulis logika tanpa pemeriksaan defensif berulang. Bila kode lain mengandalkan t.saldoSaatIni >= 0, ia tidak perlu khawatir ada jalur yang melanggar.
Referensi Lanjutan #
- Visibility Modifiers — penerapan visibility untuk menyembunyikan state internal
- Properties — kombinasi property dan accessor untuk encapsulation
Ringkasan #
Encapsulation membungkus data dan perilaku dalam satu class, menyembunyikan field internal dengan private, dan menyajikan akses melalui property publik serta method tervalidasi. Property Kotlin berperan sebagai alat bantu encapsulation karena getter dapat mengembalikan nilai tanpa mengekspos field asli. Invarian yang ditegakkan oleh class menjadi jaminan yang dapat diandalkan pemanggil tanpa pemeriksaan defensif. Pada chapter Inheritance berikutnya, kita akan melihat bagaimana encapsulation berinteraksi dengan pewarisan ketika subclass mencoba mengakses anggota superclass.