Masukkan Password
40 Factory Constructor vs Generative Constructor | Factory vs normal constructor | Dart Tutorial #39
Transcript ini membahas perbedaan paling penting antara generative constructor (constructor biasa) dan factory constructor di Dart.
Ada beberapa bagian transcript yang hasil speech-to-text-nya agak kacau, misalnya “generator” sebenarnya generative, “practical” sebenarnya factory, dan “return” kadang tertulis “written”. Saya akan luruskan sekaligus supaya konsepnya benar.
1. Gambaran besarnya dulu
Di Dart ada dua konsep constructor yang perlu kamu bedakan:
Generative constructor
Constructor biasa yang benar-benar membuat dan menginisialisasi instance baru.
Contoh:
class Person {
String name;
Person(this.name);
}
void main() {
Person p = Person("Budi");
}
Ketika:
Person("Budi")
dipanggil, Dart membuat object Person baru.
Factory constructor
Factory constructor tidak wajib membuat object baru.
Dia bertugas menentukan:
“Object mana yang harus saya kembalikan?”
Misalnya:
class Person {
final String name;
Person(this.name);
factory Person.guest() {
return Person("Guest");
}
}
Ketika:
Person.guest();
factory bisa membuat object baru dan mengembalikannya.
Tetapi factory juga bisa:
- mengembalikan object yang sudah ada,
- mengembalikan object dari cache,
- mengembalikan subclass,
- melakukan perhitungan terlebih dahulu,
- memilih object berdasarkan kondisi.
Jadi mental model paling penting:
Generative constructor = membuat/menginisialisasi object.
Factory constructor = menentukan object mana yang dikembalikan.
2. Perbedaan pertama: akses instance member
Ini adalah poin pertama dalam transcript.
Misalnya kita punya:
class Test {
int a = 10;
Test() {
a = 5;
}
}
Di sini Test() adalah generative constructor.
Kita bisa mengakses:
a = 5;
Kenapa?
Karena constructor generative bekerja pada instance yang sedang dibuat.
Secara konsep:
Test()
↓
buat object
↓
object punya field a
↓
constructor menginisialisasi object
Jadi constructor memiliki akses ke instance tersebut.
3. Bagaimana dengan factory?
Sekarang:
class Test {
int a = 10;
factory Test.create() {
a = 5; // ❌ ERROR
}
}
Ini tidak boleh.
Kenapa?
Karena factory constructor tidak memiliki instance this yang sedang diinisialisasi.
Factory belum tentu membuat object baru.
Bisa saja dia mengembalikan object yang sudah ada.
Contohnya:
factory Test.create() {
return existingTest;
}
Kalau begitu, object mana yang dimaksud oleh:
a = 5;
?
Tidak jelas.
Karena factory belum tentu mempunyai instance baru.
4. Instance member itu apa?
Sebelum lanjut, kita harus memahami istilah instance member.
Misalnya:
class Person {
String name = "Budi";
int age = 20;
void sayHello() {
print("Hello");
}
}
Yang merupakan instance member:
name → instance field
age → instance field
sayHello() → instance method
Mereka dimiliki oleh masing-masing object.
Misalnya:
Person p1 = Person();
Person p2 = Person();
Secara konsep:
p1
↓
Person object
name = Budi
age = 20
p2
↓
Person object
name = Budi
age = 20
p1.name dan p2.name adalah bagian dari instance yang berbeda.
5. Generative constructor bisa memanggil instance method
Contohnya:
class Test {
int a = 10;
void demo() {
print("Demo");
}
Test() {
a = 5;
demo();
}
}
Ini bisa dilakukan.
Constructor generative dapat mengakses instance member seperti:
a = 5;
dan:
demo();
Karena constructor tersebut bekerja terhadap object yang sedang dibuat.
6. Factory tidak bisa melakukan hal tersebut
Contoh:
class Test {
int a = 10;
void demo() {
print("Demo");
}
factory Test.create() {
a = 5; // ❌
demo(); // ❌
return Test();
}
}
Tidak boleh.
Karena factory tidak mempunyai this.
7. Apa sebenarnya this?
this berarti:
object/instance saat ini.
Misalnya:
class Person {
String name;
Person(this.name);
}
Di sini:
this.name
mengacu ke field name milik object yang sedang dibuat.
Misalnya:
Person("Budi");
Secara konsep:
Object Person
↓
name = "Budi"
8. Mengapa factory tidak punya this?
Karena factory belum tentu sedang membuat instance baru.
Contoh:
class Person {
static final Person _instance = Person._internal();
Person._internal();
factory Person() {
return _instance;
}
}
Ketika:
Person();
dipanggil, factory mengembalikan:
_instance
yang sudah ada sebelumnya.
Jadi factory bukan:
buat object baru → isi object
melainkan:
factory
↓
tentukan object
↓
return object
Itulah sebabnya factory tidak memiliki akses ke this.
9. Tiga penggunaan this yang disebut transcript
Transcript menyebut this biasanya digunakan untuk tiga hal.
Mari kita pisahkan.
A. Mengakses instance member
Contoh:
class Person {
String name = "Budi";
Person() {
this.name = "Andi";
}
}
this.name berarti field name milik object saat ini.
B. Initializing formal parameter
Contoh:
class Person {
String name;
int age;
Person(this.name, this.age);
}
Ini adalah fitur Dart yang sangat sering digunakan.
Secara konsep, ini seperti:
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Jadi:
Person(this.name, this.age);
adalah bentuk singkat.
C. Constructor redirection
Contoh:
class Person {
String name;
int age;
Person(this.name, this.age);
Person.guest() : this("Guest", 0);
}
Di sini:
this("Guest", 0)
berarti:
Constructor
Person.guest()mengarahkan proses konstruksi ke constructorPerson(...).
Tetapi hati-hati:
this.name
dan:
this(...)
adalah dua hal berbeda.
this.name
Mengakses field.
this(...)
Mengalihkan ke constructor lain dalam class yang sama.
10. Factory tidak bisa menggunakan this
Contoh:
class Test {
int a = 10;
Test() {
print(this.a); // ✅
}
factory Test.create() {
print(this.a); // ❌
}
}
Factory tidak boleh menggunakan this.
Termasuk untuk mengakses:
- instance field,
- instance method,
- initializer list,
- constructor redirection menggunakan
this(...).
11. Perbedaan kedua: return
Ini juga sangat penting.
Generative constructor tidak menggunakan return value
Misalnya:
class Person {
Person() {
return Person(); // ❌
}
}
Tidak boleh.
Constructor generative tidak boleh mengembalikan nilai secara eksplisit.
Jadi bukan seperti function:
Person createPerson() {
return Person();
}
Constructor memiliki mekanisme pembuatan object sendiri.
12. Factory justru harus mengembalikan instance
Contoh:
class Person {
factory Person.create() {
return Person();
}
}
Di sini:
return Person();
wajib karena factory harus menghasilkan sebuah instance.
Misalnya factory seperti:
factory Person.create() {
if (condition) {
return Person();
}
return Person();
}
Semua jalur harus menghasilkan instance yang sesuai.
13. Analogi sederhana return
Bayangkan constructor sebagai pabrik.
Generative constructor
Pabriknya sendiri yang membuat barang:
Pesanan
↓
Generative constructor
↓
buat barang baru
↓
barang
Kita tidak perlu mengatakan:
"Ini barang yang saya return."
Karena mekanisme constructor sudah menangani instance yang dibuat.
Factory constructor
Factory seperti petugas yang menentukan barang mana yang diberikan:
Pesanan
↓
Factory
↓
cek kondisi
├── barang baru → return
└── barang lama → return
Karena factory bertugas mengembalikan instance, maka harus ada return.
14. Perbedaan ketiga: bisa mengembalikan subclass
Ini adalah kemampuan penting dari factory.
Misalnya:
class Animal {
Animal();
}
class Cat extends Animal {
Cat();
}
class Dog extends Animal {
Dog();
}
Kita bisa membuat factory pada superclass:
class Animal {
factory Animal.create(String type) {
if (type == "cat") {
return Cat();
}
return Dog();
}
}
Kemudian:
Animal animal1 = Animal.create("cat");
Animal animal2 = Animal.create("dog");
Hasil sebenarnya bisa:
animal1 → Cat object
animal2 → Dog object
Walaupun tipe variabelnya:
Animal
15. Kenapa ini berguna?
Karena kode yang menggunakan Animal tidak perlu tahu object konkret apa yang dibuat.
Contohnya:
Animal animal = Animal.create("cat");
Dari luar kita hanya tahu:
Saya meminta
Animal.
Factory yang menentukan:
"cat" → Cat
"dog" → Dog
Ini sangat berguna dalam factory pattern.
16. Generative constructor tidak seperti itu
Generative constructor pada dasarnya menghasilkan instance dari class tempat constructor tersebut didefinisikan.
Misalnya:
class Father {
Father();
}
Ketika:
Father();
yang dibuat adalah:
Father object
Sedangkan factory dapat melakukan:
factory Father.create() {
return Son();
}
Jika Son extends Father, hasil konkretnya bisa berupa:
Son object
17. Perbedaan keempat: object baru vs object lama
Ini mungkin bagian paling penting dari seluruh video.
Generative constructor
Setiap pemanggilan constructor generative membuat instance baru.
Misalnya:
class Person {
Person();
}
void main() {
final p1 = Person();
final p2 = Person();
}
Secara konsep:
p1 ──→ Person Object #1
p2 ──→ Person Object #2
Jadi:
p1 != p2
untuk identity object.
18. Factory bisa membuat object baru
Factory juga bisa menggunakan constructor generative untuk membuat object baru.
class Person {
Person._internal();
factory Person.create() {
return Person._internal();
}
}
Setiap kali:
Person.create();
dipanggil, factory membuat object baru melalui:
Person._internal()
Jadi:
Person.create()
↓
factory
↓
Person._internal()
↓
object baru
19. Tapi factory juga bisa mengembalikan object lama
Misalnya:
class Person {
Person._internal();
static final Person _instance = Person._internal();
factory Person.getOld() {
return _instance;
}
}
Sekarang:
final p1 = Person.getOld();
final p2 = Person.getOld();
Keduanya mendapatkan object yang sama.
Secara konsep:
p1 ─────┐
↓
Person Object #1
↑
|
p2 ─────┘
Jadi:
identical(p1, p2)
akan menghasilkan:
true
Ini adalah konsep dasar Singleton.
Catatan penting: transcript beberapa kali menghubungkan identity object dengan
hashCode. Jangan gunakanhashCodeuntuk menentukan apakah dua object adalah object yang sama. Untuk identity, gunakanidentical(a, b).
20. Kenapa _instance harus static?
Perhatikan:
class Person {
Person._internal();
static final Person _instance = Person._internal();
factory Person() {
return _instance;
}
}
Kenapa:
static
?
Karena factory tidak mempunyai this.
Field biasa:
final Person _instance;
adalah instance field.
Sedangkan:
static final Person _instance;
adalah static field yang dimiliki class, bukan object tertentu.
Karena factory bisa mengakses static member, maka:
return _instance;
bisa dilakukan.
21. Perbedaan kelima: super(...)
Sekarang kita masuk ke inheritance.
Misalnya:
class Father {
Father(int age) {
print("Father age = $age");
}
}
class Son extends Father {
Son() : super(20);
}
Ini valid.
Son() adalah generative constructor.
Bagian:
super(20)
berarti:
Panggil constructor superclass
Fatheryang menerimaint.
Alurnya:
Son()
↓
super(20)
↓
Father(20)
↓
Son constructor body
22. Kenapa factory tidak bisa menggunakan super(...) seperti itu?
Contoh:
class Son extends Father {
factory Son.create() : super(20); // ❌
}
Tidak boleh.
Karena super(...) pada initializer list adalah bagian dari proses generative construction.
Factory tidak melakukan proses tersebut secara langsung.
Factory harus melakukan sesuatu seperti:
factory Son.create() {
return Son(...);
}
atau mengembalikan instance lain yang sesuai.
23. Bahkan factory superclass tidak bisa dipanggil dengan super
Misalnya:
class Father {
factory Father.create() {
return Father();
}
}
Lalu:
class Son extends Father {
Son() : super.create(); // ❌
}
Tidak bisa.
Karena constructor yang dipanggil lewat:
super(...)
harus merupakan generative constructor superclass, bukan factory constructor.
24. Perbedaan keenam: perhitungan sebelum object dibuat
Nah, ini bagian yang agak membingungkan dari transcript.
Misalnya kita punya:
class Test {
final int x;
final int y;
Test(this.x, this.y);
}
Keduanya:
final int x;
final int y;
harus mendapatkan nilai ketika object dibangun.
Sekarang misalnya:
x berasal dari user
y harus dihitung berdasarkan x
Contoh:
int calculateY(int x) {
return x * 2;
}
Kita ingin:
x = 10
y = 20
25. Dengan generative constructor
Kita bisa menggunakan initializer list:
class Test {
final int x;
final int y;
Test(int x)
: x = x,
y = calculateY(x);
}
Ini valid.
Jadi ada sedikit koreksi penting terhadap transcript:
Transcript mengatakan seolah-olah generative constructor tidak bisa melakukan kalkulasi untuk
finalfield. Yang benar, generative constructor bisa melakukan perhitungan di initializer list.
Misalnya:
int calculateY(int x) {
return x * 2;
}
class Test {
final int x;
final int y;
Test(int x)
: x = x,
y = calculateY(x);
}
Lalu:
final test = Test(10);
print(test.x); // 10
print(test.y); // 20
26. Lalu apa keuntungan factory?
Factory memberikan cara yang lebih fleksibel untuk melakukan preprocessing sebelum memanggil generative constructor.
Contohnya:
class Test {
final int x;
final int y;
Test(this.x, this.y);
factory Test.create(int x) {
final value = calculateY(x);
return Test(x, value);
}
}
Alurnya:
Test.create(10)
↓
factory
↓
calculateY(10)
↓
20
↓
Test(10, 20)
↓
object
Jadi factory bisa menjadi lapisan logika sebelum constructor generative dipanggil.
27. Bandingkan keduanya
Generative
class Test {
final int x;
final int y;
Test(int x)
: x = x,
y = calculateY(x);
}
Langsung melakukan initialization melalui initializer list.
Factory
class Test {
final int x;
final int y;
Test(this.x, this.y);
factory Test.create(int x) {
final y = calculateY(x);
return Test(x, y);
}
}
Factory melakukan logic terlebih dahulu, lalu memilih/mengembalikan instance.
Keduanya bisa valid.
Jadi jangan sampai berpikir:
“Kalau mau menghitung nilai final, wajib pakai factory.”
Tidak.
Factory hanya memberikan fleksibilitas tambahan.
28. Perbedaan mendasar: siapa yang memiliki this?
Ini cara yang sangat bagus untuk mengingat.
Generative constructor
Generative constructor
↓
sedang membangun object
↓
punya this
↓
bisa akses instance member
Sedangkan:
Factory constructor
Factory constructor
↓
belum tentu membuat object
↓
tidak punya this
↓
tidak bisa akses instance member
↓
harus return instance
29. Tabel perbandingan
| Fitur | Generative Constructor | Factory Constructor |
|---|---|---|
| Membuat instance baru | ✅ | Tidak harus |
this | ✅ | ❌ |
| Akses instance field | ✅ | ❌ |
| Akses instance method | ✅ | ❌ |
| Akses static member | ✅ | ✅ |
return instance eksplisit | ❌ | ✅ |
| Bisa return object lama | ❌ | ✅ |
| Bisa return subclass | ❌ sebagai mekanisme constructor-nya | ✅ |
| Bisa memakai initializer list | ✅ | ❌ |
Bisa redirect dengan this(...) | ✅ | ❌ |
Bisa memanggil superclass constructor dengan super(...) | ✅ | ❌ |
| Cocok untuk Singleton | Tidak secara langsung | ✅ |
| Cocok untuk caching | Tidak secara langsung | ✅ |
| Cocok untuk Factory Pattern | Tidak secara langsung | ✅ |
30. Contoh lengkap agar benar-benar terasa bedanya
Generative constructor
class Person {
final String name;
Person(this.name);
}
void main() {
final p1 = Person("Budi");
final p2 = Person("Budi");
print(identical(p1, p2));
}
Hasil:
false
Karena:
Person("Budi")
↓
object baru
Person("Budi")
↓
object baru lagi
Ada dua object.
31. Factory dengan cache
Sekarang:
class Person {
final String name;
Person._internal(this.name);
static final Map<String, Person> _cache = {};
factory Person(String name) {
if (_cache.containsKey(name)) {
return _cache[name]!;
}
final person = Person._internal(name);
_cache[name] = person;
return person;
}
}
Sekarang:
void main() {
final p1 = Person("Budi");
final p2 = Person("Budi");
print(identical(p1, p2));
}
Hasil:
true
Kenapa?
Pemanggilan pertama:
Person("Budi")
↓
belum ada di cache
↓
buat object
↓
simpan ke cache
Pemanggilan kedua:
Person("Budi")
↓
sudah ada di cache
↓
ambil object lama
Tidak membuat object kedua.
32. Contoh factory mengembalikan subclass
abstract class Animal {
factory Animal.create(String type) {
if (type == "cat") {
return Cat();
}
if (type == "dog") {
return Dog();
}
throw ArgumentError("Unknown animal");
}
}
class Cat extends Animal {}
class Dog extends Animal {}
Kemudian:
final animal1 = Animal.create("cat");
final animal2 = Animal.create("dog");
Secara runtime:
animal1 → Cat
animal2 → Dog
Tetapi dari sisi tipe:
animal1 : Animal
animal2 : Animal
Ini sangat berguna ketika kita ingin menyembunyikan detail proses pembuatan object.
33. Kapan sebaiknya menggunakan generative constructor?
Gunakan constructor biasa ketika:
“Saya memang ingin membuat instance dari class ini dan menginisialisasinya.”
Contoh:
class User {
final String name;
final int age;
User(this.name, this.age);
}
Ini adalah penggunaan yang sangat normal.
Di Flutter, pola seperti ini sangat umum.
34. Kapan menggunakan factory?
Factory lebih cocok ketika proses pembuatan object membutuhkan keputusan atau logic tambahan.
Misalnya:
1. Singleton
factory Database() {
return _instance;
}
2. Cache
factory User(String id) {
return cache[id] ?? createNewUser(id);
}
3. Memilih subclass
factory Animal(String type) {
if (type == "cat") return Cat();
return Dog();
}
4. Parsing data
Contohnya:
factory User.fromJson(Map<String, dynamic> json) {
return User(
json["name"],
json["age"],
);
}
5. Preprocessing
factory User.create(String username) {
final normalized = username.trim().toLowerCase();
return User(normalized);
}
35. Satu hal yang sangat penting: factory bukan berarti selalu membuat object
Ini sering menjadi kesalahan pemula.
Jangan berpikir:
factory = constructor yang membuat object
Lebih tepat:
factory = constructor yang menghasilkan/mengembalikan instance
Instance tersebut bisa:
object baru
atau:
object lama
atau:
object subclass
atau object yang berasal dari cache.
36. Analogi paling sederhana
Bayangkan kamu datang ke restoran.
Generative constructor
Kamu pesan:
“Buatkan saya nasi goreng.”
Dapur:
pesanan
↓
masak makanan baru
↓
makanan baru
Factory constructor
Kamu bilang:
“Saya mau makanan untuk pesanan nomor 123.”
Petugas mengecek:
pesanan
↓
factory
↓
apakah sudah ada?
├── ya → ambil makanan lama
└── tidak → buat makanan baru
Bahkan factory bisa berkata:
“Untuk jenis pesanan ini, kamu sebenarnya mendapatkan menu khusus.”
Itulah gambaran factory yang dapat mengembalikan subclass.
37. Ringkasan seluruh video
Kalau kamu hanya ingin mengingat 7 hal, ingat ini:
1. Generative constructor membuat object
Person("Budi");
→ membuat instance Person baru.
2. Factory tidak harus membuat object baru
factory Person() {
return _instance;
}
→ bisa mengembalikan object lama.
3. Generative punya this
Karena sedang membangun instance:
Person(this.name);
Bisa mengakses:
this.name
dan:
someMethod();
4. Factory tidak punya this
Karena factory belum tentu memiliki instance yang sedang dibangun.
factory Person.create() {
// this.name ❌
// someMethod() ❌
}
5. Factory harus mengembalikan instance
factory Person.create() {
return Person();
}
6. Factory bisa mengembalikan object berbeda
Misalnya:
Animal factory
↓
"cat" → Cat
"dog" → Dog
7. Generative constructor lebih sederhana; factory lebih fleksibel
Gunakan:
Generative
→ "Saya ingin membuat object ini."
Gunakan:
Factory
→ "Saya ingin menentukan object mana yang harus diberikan."
38. Cheat sheet terakhir
// GENERATIVE
class Person {
final String name;
Person(this.name);
}
Artinya:
Buat dan inisialisasi
Personbaru.
// FACTORY
class Person {
final String name;
Person(this.name);
factory Person.guest() {
return Person("Guest");
}
}
Artinya:
Tentukan instance
Personyang akan dikembalikan.
Kalimat paling penting untuk dihafalkan:
Generative constructor membangun instance, sedangkan factory constructor menentukan instance mana yang akan dikembalikan.
Dan ada satu koreksi penting dari transcript: factory bukan satu-satunya cara untuk menghitung nilai final sebelum object dibuat. Generative constructor juga bisa melakukan kalkulasi melalui initializer list. Keunggulan factory adalah ia bisa melakukan logic lebih bebas sebelum memilih dan mengembalikan instance.
Kalau kamu ingin, saya juga bisa membuat gambar alur visual Generative vs Factory Constructor supaya perbedaannya lebih mudah diingat.