Mixin
Berbagi perilaku antar class tanpa inheritance dengan mixin.
Berbagi perilaku tanpa hierarki
Inheritance bagus untuk hubungan "adalah", tapi kaku: Dart hanya boleh satu induk. Bagaimana kalau Kucing butuh perilaku BisaBerenang DAN BisaMenyelam? Mixin memungkinkan satu class "mencampur" perilaku dari banyak sumber tanpa hierarki.
Analogi: inheritance itu seperti garis keturunan keluarga (satu ayah). Mixin itu seperti kursus keterampilan: siapa saja boleh ikut kursus berenang, memasak, dan bela diri sekaligus, tanpa mengubah keluarganya.
Kenapa mixin ada?
Masalah klasik OOP: perilaku yang cross-cutting (dibutuhkan banyak class yang tidak berkerabat). Tanpa mixin, pilihannya buruk: duplikasi kode di tiap class, atau hierarki artifisial yang dipaksakan. Mixin memberi jalan ketiga yang bersih.
with: memakai mixin
// Mixin: kumpulan method/properti tanpa constructor
mixin BisaBerenang {
void berenang() => print('Berenang dengan gaya bebas!');
}
mixin BisaTerbang {
void terbang() => print('Terbang tinggi ke angkasa!');
}
class Bebek with BisaBerenang, BisaTerbang {
String nama;
Bebek(this.nama);
}
class Ikan with BisaBerenang {
String nama;
Ikan(this.nama);
}
void main() {
var donal = Bebek('Donal');
donal.berenang(); // dari mixin
donal.terbang(); // dari mixin
var nemo = Ikan('Nemo');
nemo.berenang();
// nemo.terbang(); // ERROR: Ikan tidak punya mixin itu
}with mencampurkan semua anggota mixin ke class. Bebek dapat dua perilaku, Ikan satu, tanpa hubungan keluarga di antara mereka.
Contoh kedua, mixin dengan state dan method konkret:
mixin Pencatat {
final List<String> _log = [];
void catat(String pesan) {
var waktu = DateTime.now().toIso8601String();
_log.add('[$waktu] $pesan');
}
void tampilkanLog() {
for (var l in _log) {
print(l);
}
}
}
class Kasir with Pencatat {
void bayar(int jumlah) {
catat('Pembayaran $jumlah diterima');
print('Terima kasih!');
}
}
void main() {
var k = Kasir();
k.bayar(50000);
k.bayar(25000);
k.tampilkanLog();
}Mixin boleh punya field dan method konkret. Setiap class yang memakai mixin mendapat salinan state-nya sendiri.
Batasan mixin: on
Kadang mixin hanya masuk akal untuk class tertentu. Klausa on membatasi:
class Hewan {
String nama;
Hewan(this.nama);
}
mixin Peliharaan on Hewan {
void dipanggil() => print('$nama datang!'); // akses field Hewan
}
class Kucing extends Hewan with Peliharaan {
Kucing(String nama) : super(nama);
}
void main() {
Kucing('Kitty').dipanggil(); // Kitty datang!
// class Robot with Peliharaan {} // ERROR: Robot bukan Hewan
}on Hewan artinya "mixin ini hanya untuk keturunan Hewan", sehingga mixin boleh mengakses anggota Hewan dengan aman.
Kesalahan umum
1. Mixin dengan constructor
// SALAH
// mixin Contoh {
// Contoh(this.x); // ERROR: mixin tidak boleh punya constructor
// int x;
// }// BENAR: mixin hanya deklarasi anggota
mixin Contoh {
int x = 0;
void tambah() => x++;
}2. Konflik nama antar mixin
mixin A {
void sapa() => print('Halo dari A');
}
mixin B {
void sapa() => print('Halo dari B');
}
class C with A, B {} // B menang: yang TERAKHIR menang
void main() {
C().sapa(); // Halo dari B
}Jika dua mixin punya anggota sama, yang ditulis terakhir menang (linearization). Hindari mengandalkan ini; beri nama yang jelas.
3. Memakai mixin untuk hubungan "adalah"
// Kurang tepat: Kucing ADALAH Hewan, pakai extends
// class Kucing with HewanMixin {}// BENAR
class Kucing extends Hewan with BisaBerenang {
Kucing(String nama) : super(nama);
}Aturan: extends untuk identitas, with untuk kemampuan tambahan. Keduanya bisa digabung.
Kesimpulan
Mixin = paket perilaku reusable tanpa hierarki, dipakai dengan with, dibatasi dengan on. Ideal untuk kemampuan cross-cutting (logging, validasi, serialisasi) yang dibutuhkan banyak class tak berkerabat.
Catatan teknis: Sejak Dart 3,
mixin classmemungkinkan satu deklarasi dipakai sebagai mixin MAUPUN class biasa, menggabungkan fleksibilitas keduanya.
Tantangan
Mixin terbang
Buat mixin BisaTerbang dengan method terbang(). Tempelkan ke class Pesawat dan Superhero (keduanya punya field nama). Panggil terbang() untuk keduanya.
// TODO: mixin + 2 class
void main() {
// TODO
}