dartoopconstructorMenengah5 mnt baca

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

dart
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.

dart
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:

dart
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

dart
// 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

dart
// 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
}
dart
// 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.

text
// TODO: class Rekening

void main() {
  // TODO
}