Named & Factory Constructor
Banyak cara membuat object: named constructor dan factory.
Banyak pintu masuk untuk satu class
Satu class kadang perlu dibuat dengan cara berbeda: Siswa dari input manual, dari data JSON API, atau dari database. Menjejalkan semua logika ke satu constructor bikin kacau. Dart memberi dua solusi: named constructor (beberapa constructor bernama) dan factory constructor (constructor dengan logika khusus).
Analogi: gedung punya pintu utama, pintu samping, dan pintu darurat. Semua menuju gedung yang sama, tapi untuk kebutuhan berbeda. Named constructor adalah pintu-pintu itu.
Kenapa named constructor ada?
Karena satu constructor tidak cukup ekspresif. Siswa('Budi', 17) vs Siswa.dariJson(json): yang kedua langsung menjelaskan ASAL datanya. Kode yang menjelaskan dirinya sendiri adalah kode yang bagus.
Named constructor
class Siswa {
String nama;
int umur;
Siswa(this.nama, this.umur); // constructor utama
// Named constructor: nama tambahan setelah titik
Siswa.baruLahir(this.nama) : umur = 0;
Siswa.dariJson(Map<String, dynamic> json)
: nama = json['nama'],
umur = json['umur'];
void info() => print('$nama, $umur tahun');
}
void main() {
var a = Siswa('Budi', 17);
var bayi = Siswa.baruLahir('Cici');
var dariApi = Siswa.dariJson({'nama': 'Dedi', 'umur': 20});
a.info();
bayi.info(); // Cici, 0 tahun
dariApi.info(); // Dedi, 20 tahun
}Tiap named constructor punya initializer list sendiri. Pola Siswa.dariJson sangat standar untuk parsing API di Flutter.
Factory constructor: constructor yang bisa berpikir
Constructor biasa SELALU membuat objek baru. Factory boleh tidak: ia bisa mengembalikan objek lama (cache), atau memilih subclass yang tepat. Ditandai kata kunci factory, dan tidak bisa memakai this.x di parameter.
class Logger {
final String nama;
static final Map<String, Logger> _cache = {};
// Factory: kembalikan yang sudah ada jika namanya sama
factory Logger(String nama) {
return _cache.putIfAbsent(nama, () => Logger._internal(nama));
}
Logger._internal(this.nama); // constructor privat (nama diawali _)
void log(String pesan) => print('[$nama] $pesan');
}
void main() {
var a = Logger('app');
var b = Logger('app');
print(identical(a, b)); // true, objeknya SAMA (dari cache)
a.log('mulai');
b.log('lanjut'); // [app] lanjut, karena a dan b satu objek
}Contoh kedua yang lebih sederhana, factory memilih implementasi:
abstract class Hewan {
void bersuara();
factory Hewan(String jenis) {
if (jenis == 'kucing') return Kucing();
return Anjing();
}
}
class Kucing implements Hewan {
void bersuara() => print('Meong');
}
class Anjing implements Hewan {
void bersuara() => print('Guk guk');
}
void main() {
Hewan('kucing').bersuara(); // Meong
Hewan('anjing').bersuara(); // Guk guk
}Pemanggil cukup tahu Hewan(...), factory yang memutuskan class konkretnya. Ini pola factory pattern yang terkenal.
Kesalahan umum
1. this.x di factory constructor
// SALAH
class Contoh {
String nama;
// factory Contoh(this.nama) => Contoh._(nama); // ERROR
factory Contoh(String nama) => Contoh._(nama);
Contoh._(this.nama);
}Factory tidak menginisialisasi field langsung, ia mendelegasikan ke constructor lain.
2. Named constructor memanggil named lain dengan salah
// SALAH: tidak bisa pakai this() seperti Java
class Siswa {
String nama;
int umur;
Siswa(this.nama, this.umur);
// Siswa.bayi(String n) : this(n, 0); // ERROR di Dart lama
}// BENAR: redirecting constructor
class Siswa {
String nama;
int umur;
Siswa(this.nama, this.umur);
Siswa.bayi(String n) : this(n, 0); // OK: redirect ke constructor utama
}3. Lupa factory tidak otomatis membuat instance baru
Jangan pakai factory kalau hanya butuh constructor biasa. Factory untuk: cache/singleton, memilih subclass, atau logika yang tidak bisa di initializer list.
Kesimpulan
Named constructor = banyak pintu masuk dengan nama jelas (.dariJson, .bayi). Factory = constructor yang bisa berpikir (cache, pilih subclass). Keduanya membuat pembuatan objek ekspresif dan terkendali.
Catatan teknis: Constructor privat (
_internal) hanya bisa dipanggil dari file yang sama. Ini fondasi pola singleton dan enkapsulasi pembuatan objek.
Tantangan
Rekening dengan banyak cara buat
Buat class Rekening (field: nama, saldo) dengan constructor biasa, named Rekening.baru(nama) (saldo 0), dan named Rekening.vip(nama) (saldo 1000000). Cetak ketiganya.
// TODO: class Rekening
void main() {
// TODO
}