Enkapsulasi, Getter & Setter
Menyembunyikan data dengan _private, getter, dan setter.
Menjaga data dari tangan jahil
Sejauh ini field class bisa diubah siapa saja dari mana saja: rekening.saldo = -999999. Di dunia nyata itu bencana. Enkapsulasi adalah prinsip membungkus data agar hanya bisa diakses lewat "pintu resmi" yang terkontrol.
Analogi: brankas bank. Kamu tidak mengambil uang langsung dari tumpukan, tapi lewat teller yang memverifikasi identitas dan mencatat transaksi. Getter/setter adalah teller-nya objek.
Kenapa enkapsulasi ada?
Tiga alasan: validasi (tolak nilai tidak masuk akal), fleksibilitas internal (ubah cara simpan tanpa merusak pemakai), dan read-only (data boleh dibaca tapi tidak ditulis sembarangan).
Private dengan underscore
Di Dart, privasi berbasis file, bukan class: nama diawali _ hanya bisa diakses dari file yang sama.
class Rekening {
String pemilik;
double _saldo = 0; // privat: hanya file ini yang bisa sentuh langsung
Rekening(this.pemilik, this._saldo);
void setor(double jumlah) {
if (jumlah > 0) {
_saldo += jumlah;
print('Setor $jumlah berhasil.');
} else {
print('Jumlah setor harus positif!');
}
}
bool tarik(double jumlah) {
if (jumlah > 0 && jumlah <= _saldo) {
_saldo -= jumlah;
return true;
}
print('Penarikan gagal: jumlah tidak valid atau saldo kurang.');
return false;
}
}
void main() {
var r = Rekening('Budi', 100000);
r.setor(50000); // OK
r.setor(-10); // ditolak dengan pesan
r.tarik(200000); // ditolak: saldo kurang
// r._saldo = 999999; // ERROR jika di file berbeda
}Method setor/tarik adalah pintu resmi: semua perubahan saldo lewat validasi. Tidak ada jalan pintas mengubah saldo sembarangan.
Getter dan setter: properti terkontrol
Kadang kamu ingin akses terasa seperti field biasa (r.saldo) tapi tetap lewat validasi. Itulah getter dan setter:
class Rekening {
String pemilik;
double _saldo;
Rekening(this.pemilik, this._saldo);
// Getter: baca saldo seperti properti biasa
double get saldo => _saldo;
// Setter: tulis dengan validasi
set saldo(double nilai) {
if (nilai < 0) {
print('Saldo tidak boleh negatif!');
return;
}
_saldo = nilai;
}
// Getter komputasi: tanpa field penyimpan
String get status => _saldo >= 100000 ? 'Sultan' : 'Biasa';
}
void main() {
var r = Rekening('Budi', 50000);
print(r.saldo); // 50000.0, lewat getter
print(r.status); // Biasa
r.saldo = 200000; // lewat setter
print(r.status); // Sultan
r.saldo = -50; // ditolak setter
print(r.saldo); // tetap 200000.0
}Pemakai menulis r.saldo seperti field biasa, tapi di baliknya ada logika. Getter komputasi (status) bahkan tidak butuh field penyimpan.
Kapan pakai apa?
class Contoh {
// 1. Field publik: data sederhana tanpa aturan
String nama = '';
// 2. Private + getter saja: read-only dari luar
final String _id;
Contoh(this._id);
String get id => _id;
// 3. Private + getter + setter: butuh validasi
int _umur = 0;
int get umur => _umur;
set umur(int u) {
if (u >= 0) _umur = u;
}
// 4. Method: operasi/aksi, bukan sekadar akses data
void ulangTahun() => _umur++;
}Aturan jempol: mulai dari field publik untuk hal sepele, naik ke getter/setter saat butuh validasi atau read-only.
Kesalahan umum
1. Mengira _ privat antar class dalam satu file
// File yang SAMA: _saldo BISA diakses!
void main() {
var r = Rekening('Budi', 1000);
// print(r._saldo); // TIDAK ERROR jika main di file yang sama
}Privasi Dart = level file. Untuk proteksi antar file, pisahkan class ke file sendiri. Di DartPad (satu file), _ tidak memberi proteksi nyata, tapi tetap praktik yang baik.
2. Setter tanpa validasi = field publik yang ribet
// Tidak ada gunanya
// set umur(int u) => _umur = u; // sama saja field publikJangan buat getter/setter sekadar gaya. Kalau tidak ada logika tambahan, field publik lebih jujur dan ringkas.
3. Getter dengan efek samping
// SALAH: getter seharusnya tidak mengubah state
// int get hitung {
// _counter++;
// return _counter;
// }Getter dibaca seperti properti, pembaca tidak menyangka ia mengubah data. Efek samping milik method.
Kesimpulan
Enkapsulasi = _ untuk privat + method/getter/setter sebagai pintu resmi. Tujuannya bukan mempersulit, tapi menjamin data selalu valid dan perubahan internal tidak merusak pemakai class.
Catatan teknis: Di Dart 3.x tidak ada kata kunci
private/publicseperti Java. Konvensinya murni lewat underscore, sederhana tapi efektif untuk privasi level library.
Tantangan
Suhu dengan validasi
Buat class Suhu dengan field privat _celsius, setter yang menolak di bawah -273.15 (nol mutlak), getter celsius, dan getter komputasi fahrenheit (c*9/5+32).
// TODO: class Suhu
void main() {
// TODO: set 25, cetak celsius & fahrenheit. Coba set -300 (harus throw).
}