Masukkan Password
47 Konstruktor Pribadi & Pola Singleton
Materi ini membahas private constructor dan kaitannya dengan Singleton Pattern. Ini memang sedikit lebih abstrak dibanding public/private biasa, jadi saya akan mulai dari konsep paling dasar lalu masuk ke implementasinya.
Materi sumber menjelaskan bahwa constructor yang dibuat private tidak bisa dipanggil dari luar class, lalu teknik ini digunakan untuk membatasi pembuatan instance dan membangun Singleton Pattern.
1. Ingat dulu: apa itu constructor?
Sebelumnya kita sudah belajar bahwa constructor adalah bagian dari class yang dijalankan ketika kita menggunakan new.
Contoh:
class Person {
constructor() {
console.log("Person dibuat");
}
}
Kemudian:
const person = new Person();
Yang terjadi:
new Person()
↓
constructor() dijalankan
↓
object Person dibuat
↓
object disimpan ke person
Jadi new Person() pada dasarnya meminta:
“Tolong buatkan saya object baru dari class
Person.”
2. Constructor secara default adalah public
Kalau kita menulis:
class Person {
constructor() {
}
}
TypeScript menganggap constructor tersebut bisa dipanggil dari luar class.
Secara konsep:
class Person {
public constructor() {
}
}
Sehingga kita boleh:
const person1 = new Person();
const person2 = new Person();
const person3 = new Person();
Artinya setiap kali kita menggunakan:
new Person()
kita bisa membuat object baru.
3. Bagaimana kalau constructor dibuat private?
Sekarang kita ubah:
class Person {
private constructor() {
}
}
Sekarang kita coba:
const person = new Person();
Akan error.
Kurang lebih error-nya:
Constructor of class 'Person' is private
and only accessible within the class declaration.
Artinya:
Constructor
Personbersifat private, sehingga hanya boleh digunakan dari dalam classPersonitu sendiri.
Materi juga menekankan bahwa setelah constructor dibuat private, kita tidak bisa membuat instance Person secara langsung dari luar class.
4. Kenapa kita sengaja melakukan itu?
Pertanyaan pentingnya:
“Kalau constructor private membuat kita tidak bisa
new Person(), lalu buat apa?”
Nah, inilah tujuan utamanya.
Dengan private constructor, kita bisa mengontrol bagaimana object dari sebuah class dibuat.
Biasanya:
DARI LUAR CLASS
new Person()
↓
Person object
Dengan private constructor:
DARI LUAR CLASS
new Person()
❌
Kita kemudian bisa membuat mekanisme khusus:
DARI LUAR CLASS
Person.getInstance()
↓
Person object
Jadi class tersebut mengatakan:
“Kamu tidak boleh membuat object saya sembarangan. Kalau mau mendapatkan object saya, gunakan cara yang saya sediakan.”
Inilah yang kemudian digunakan oleh Singleton Pattern.
5. Apa itu Singleton Pattern?
Singleton Pattern adalah pola desain yang digunakan ketika kita ingin memastikan:
Sebuah class hanya memiliki satu instance yang digunakan bersama.
Misalnya kita punya:
class Person
Biasanya kita bisa membuat:
const person1 = new Person();
const person2 = new Person();
const person3 = new Person();
Artinya:
Person
├── person1
├── person2
└── person3
Ada 3 object.
Tetapi dengan Singleton, kita ingin:
Person
│
└── hanya 1 object
↑
person1
person2
person3
Ketiga variable tersebut menunjuk ke object yang sama.
6. Analogi sederhana
Bayangkan ada sebuah ruang kontrol utama di sebuah perusahaan.
Kita tidak ingin setiap bagian membuat ruang kontrol sendiri-sendiri.
Kita hanya ingin:
🏢 Ruang Kontrol Utama
↑
┌────┼────┐
│ │ │
Tim A Tim B Tim C
Semua menggunakan ruang kontrol yang sama.
Singleton kurang lebih mempunyai ide seperti itu:
Class
↓
Satu instance
↓
Digunakan bersama
7. Langkah pertama Singleton: private constructor
Misalnya:
class Person {
private constructor() {
}
}
Sekarang:
new Person();
tidak boleh dilakukan dari luar.
Ini penting karena kalau constructor masih public, orang masih bisa membuat object sebanyak-banyaknya.
Misalnya:
const person1 = new Person();
const person2 = new Person();
const person3 = new Person();
Singleton jadi gagal.
Jadi langkah pertama:
private constructor
↓
cegah orang membuat instance langsung
Materi juga menyebut bahwa membuat constructor private adalah langkah pertama dalam implementasi Singleton Pattern.
8. Kalau tidak boleh new Person(), bagaimana mendapatkan object?
Kita buat method khusus.
Misalnya:
class Person {
private constructor() {
}
getInstance() {
return new Person();
}
}
Ide sederhananya:
getInstance()
↓
new Person()
↓
return object
Tetapi masih ada masalah.
9. Masalah pertama: getInstance() adalah instance method
Kalau kita menulis:
getInstance() {
return new Person();
}
method tersebut adalah instance method.
Untuk memanggilnya, kita membutuhkan object:
person.getInstance();
Tetapi masalahnya:
Kita tidak punya object
person, karena constructor-nya private!
Kita mengalami lingkaran:
Butuh object
↓
untuk memanggil getInstance()
↓
tetapi object hanya bisa dibuat melalui constructor
↓
constructor private
↓
tidak bisa membuat object dari luar
Maka solusinya adalah membuat getInstance() menjadi static method.
10. Jadikan getInstance() sebagai static method
Kita ubah menjadi:
class Person {
private constructor() {
}
static getInstance() {
return new Person();
}
}
Sekarang kita tidak perlu membuat object terlebih dahulu.
Kita bisa:
const person = Person.getInstance();
Perhatikan:
Person.getInstance()
bukan:
person.getInstance()
Karena getInstance() adalah static method.
11. Tapi masih ada masalah!
Sekarang kita memang bisa mendapatkan object:
const person1 = Person.getInstance();
Tetapi bagaimana kalau kita panggil lagi?
const person2 = Person.getInstance();
Dengan kode:
static getInstance() {
return new Person();
}
setiap pemanggilan akan menjalankan:
new Person()
Jadi:
Person.getInstance()
↓
new Person()
↓
Object A
Person.getInstance()
↓
new Person()
↓
Object B
Akibatnya:
person1 !== person2
Ini bukan Singleton.
12. Kita membutuhkan tempat untuk menyimpan instance
Solusinya adalah membuat static property.
Misalnya:
private static instance: Person | null = null;
Mari kita pecah:
private
Artinya property ini hanya boleh diakses dari dalam class.
static
Artinya property ini milik class, bukan milik masing-masing object.
instance
adalah nama property-nya.
Person | null
berarti nilainya bisa berupa:
- object
Person, atau null
Pada awalnya:
instance = null
karena object belum dibuat.
13. Bentuk class-nya sekarang
Secara sederhana:
class Person {
private static instance: Person | null = null;
private constructor() {
}
static getInstance() {
// ...
}
}
Kita sekarang memiliki tiga komponen penting:
Person
│
├── private constructor()
│
├── private static instance
│
└── static getInstance()
Masing-masing mempunyai tugas berbeda.
private constructor
Mencegah:
new Person()
dari luar.
static instance
Menyimpan satu-satunya object.
static getInstance()
Menjadi pintu untuk mendapatkan object tersebut.
14. Bagaimana getInstance() bekerja?
Sekarang kita membuat:
static getInstance() {
if (Person.instance) {
return Person.instance;
}
Person.instance = new Person();
return Person.instance;
}
Mari kita pahami perlahan.
Kondisi pertama: instance belum ada
Pada awal program:
Person.instance
berisi:
null
Kemudian kita memanggil:
const person1 = Person.getInstance();
Masuk ke:
if (Person.instance) {
Karena:
Person.instance === null
maka kondisi tersebut:
false
Jadi if dilewati.
Kemudian:
Person.instance = new Person();
Sekarang kita membuat object.
Person.instance
↓
┌─────────┐
│ Person │
│ object │
└─────────┘
Lalu:
return Person.instance;
Object tersebut diberikan kepada:
person1
Jadi:
Person.instance ─────┐
↓
Object A
↑
│
person1
15. Ketika dipanggil untuk kedua kalinya
Sekarang:
const person2 = Person.getInstance();
Person.instance sudah berisi object.
Person.instance
↓
Object A
Maka:
if (Person.instance) {
return Person.instance;
}
kondisinya:
true
Jadi langsung:
return Person.instance;
Tidak menjalankan:
new Person()
lagi.
Akibatnya:
person1 ─────┐
↓
Object A
↑
│
person2 ─────┘
person1 dan person2 menunjuk object yang sama.
16. Inilah inti Singleton
Misalnya kita melakukan:
const person1 = Person.getInstance();
const person2 = Person.getInstance();
const person3 = Person.getInstance();
Yang terjadi:
Person.instance
│
↓
┌───────┐
│Object A│
└───────┘
↑ ↑ ↑
│ │ │
person1 │
person2
│
person3
Hanya ada satu object.
17. Bagaimana membuktikannya?
Kita bisa menggunakan:
console.log(person1 === person2);
Hasil:
true
Kemudian:
console.log(person2 === person3);
Hasil:
true
Artinya ketiganya mereferensikan object yang sama.
Perhatikan kita menggunakan:
===
bukan sekadar membandingkan isi.
18. Kenapa === menghasilkan true?
Misalnya:
const person1 = Person.getInstance();
const person2 = Person.getInstance();
Kalau Singleton bekerja dengan benar:
person1 ────┐
↓
Object A
↑
person2 ────┘
Maka:
person1 === person2
adalah:
true
Karena keduanya menunjuk object yang sama.
Ini berbeda dengan:
const person1 = {};
const person2 = {};
Walaupun bentuknya sama-sama object kosong:
person1 === person2
hasilnya:
false
karena keduanya adalah dua object berbeda.
19. Bentuk implementasi lengkap
Versi yang mudah dipahami:
class Person {
private static instance: Person | null = null;
private constructor() {
}
static getInstance(): Person {
if (Person.instance) {
return Person.instance;
}
Person.instance = new Person();
return Person.instance;
}
}
Kemudian:
const person1 = Person.getInstance();
const person2 = Person.getInstance();
console.log(person1 === person2);
Hasil:
true
20. Kenapa instance harus static?
Ini juga sangat penting.
Kita ingin instance menjadi tempat penyimpanan satu object untuk seluruh class.
Kalau instance bukan static, maka setiap object punya instance sendiri.
Padahal Singleton justru membutuhkan satu tempat penyimpanan bersama.
Jadi:
private static instance
bisa kita pahami sebagai:
“Class
Personmempunyai satu tempat penyimpanan yang digunakan untuk menyimpan instance Person.”
21. Kenapa instance dibuat private?
Karena kita tidak ingin kode dari luar melakukan hal seperti:
Person.instance = ...
Kita ingin hanya class Person yang mengontrol instance tersebut.
Maka:
private static instance
membuat aksesnya dikontrol dari dalam class.
Secara konsep:
Person
│
┌──────────┴──────────┐
│ │
private instance public getInstance()
│ │
│ │
└────── dikontrol ────┘
User dari luar hanya tahu:
Person.getInstance()
Mereka tidak perlu tahu bagaimana instance disimpan.
22. Ini sebenarnya menerapkan prinsip “controlled access”
Perhatikan perbedaannya.
Tanpa private constructor:
const p1 = new Person();
const p2 = new Person();
const p3 = new Person();
Semua orang bebas membuat object.
Dengan Singleton:
const p1 = Person.getInstance();
const p2 = Person.getInstance();
const p3 = Person.getInstance();
Semua orang mendapatkan object melalui satu pintu:
Person.getInstance()
│
┌──────────┴──────────┐
│ │
instance ada instance belum ada
│ │
return instance buat instance
│ │
└──────────┬──────────┘
↓
return object
Jadi getInstance() bertindak seperti gerbang.
23. Pola Singleton secara keseluruhan
Ada tiga bagian yang harus kamu ingat:
① Private constructor
private constructor() {}
Tujuan:
Mencegah pembuatan object langsung dari luar.
② Private static instance
private static instance: Person | null = null;
Tujuan:
Menyimpan satu instance yang digunakan bersama.
③ Static getInstance()
static getInstance(): Person {
if (Person.instance) {
return Person.instance;
}
Person.instance = new Person();
return Person.instance;
}
Tujuan:
Menyediakan satu pintu untuk mendapatkan instance.
24. Analogi “kartu akses”
Bayangkan sebuah gedung hanya memiliki satu ruang server.
Kita tidak mau setiap orang membuat ruang server baru.
Maka:
private constructor
↓
Tidak boleh membuat ruang server sendiri
Kemudian ada:
getInstance()
sebagai pintu masuk.
Kalau ruang server belum ada:
getInstance()
↓
buat ruang server
↓
simpan
Kalau sudah ada:
getInstance()
↓
ruang server sudah ada
↓
kembalikan ruang yang sama
Itulah Singleton.
25. Kapan Singleton digunakan?
Materi menekankan bahwa Singleton tidak selalu digunakan dan bukan sesuatu yang harus dipakai setiap kali membuat class. Ia hanya cocok untuk use case tertentu.
Contoh situasi yang bisa membutuhkan satu instance:
- konfigurasi aplikasi yang ingin digunakan bersama
- manager tertentu
- service tertentu yang memang harus memiliki satu instance
- resource yang sengaja dikontrol agar hanya memiliki satu instance
Tetapi jangan berpikir:
“Kalau bisa Singleton, berarti harus Singleton.”
Tidak.
Singleton adalah design pattern, yaitu pola solusi untuk kebutuhan tertentu.
26. Hubungannya dengan materi sebelumnya
Ini sebenarnya menggabungkan beberapa konsep TypeScript yang sudah kita pelajari.
private
Mengontrol siapa yang boleh mengakses sesuatu.
private constructor()
static
Membuat sesuatu berada pada level class.
static instance
static getInstance()
new
Membuat instance:
new Person()
Tetapi karena constructor private, new Person() hanya boleh dilakukan dari dalam Person.
Reference object
Beberapa variable bisa menunjuk object yang sama:
person1 ──┐
↓
Object A
↑
person2 ──┘
27. Jangan salah paham: static saja tidak membuat Singleton
Ini kesalahan yang cukup umum.
Misalnya:
class Person {
static getInstance() {
return new Person();
}
}
Kita memang bisa memanggil:
const p1 = Person.getInstance();
const p2 = Person.getInstance();
Tetapi:
p1 === p2
akan bernilai:
false
karena setiap kali:
Person.getInstance()
dipanggil, kita melakukan:
new Person()
yang membuat object baru.
Jadi:
static
≠
Singleton
static hanya membuat sesuatu berada di level class.
Untuk Singleton kita membutuhkan mekanisme penyimpanan instance dan pengecekan apakah instance sudah ada.
28. Mental model yang paling penting
Kalau kamu hanya ingin mengingat satu diagram, ingat ini:
Person CLASS
│
┌───────────┼───────────┐
│ │ │
private private static
constructor instance getInstance()
│ │ │
│ │ │
│ └────┬──────┘
│ │
│ cek instance
│ │
│ ┌───────┴────────┐
│ │ │
│ sudah ada belum ada
│ │ │
│ ↓ ↓
│ return new Person()
│ instance │
│ ↓
│ simpan ke instance
│ │
│ ↓
│ return instance
│
└─── mencegah new dari luar
29. Ringkasan super singkat
Private constructor:
private constructor() {}
→ Tidak bisa melakukan:
new Person()
dari luar class.
Static property:
private static instance
→ Tempat menyimpan satu instance yang dimiliki bersama oleh class.
Static method:
static getInstance()
→ Bisa dipanggil tanpa membuat object terlebih dahulu.
Singleton Pattern:
private constructor
+
static instance
+
static getInstance()
↓
satu instance untuk class tersebut
Dan bukti sederhananya:
const person1 = Person.getInstance();
const person2 = Person.getInstance();
console.log(person1 === person2);
hasil:
true
Kalimat yang paling mudah untuk diingat:
private constructormencegah orang membuat object sembarangan, sedangkangetInstance()menjadi pintu resmi untuk mendapatkan satu object yang sama.
Itulah inti dari Singleton Pattern yang dijelaskan pada materi ini.