Masukkan Password
73 Decorator Factory
Pada lecture ini fokusnya adalah Decorator Factory, yaitu cara membuat decorator yang bisa menerima parameter/configuration dari luar.
Kalau lecture sebelumnya membahas:
@logger
maka lecture ini naik satu tingkat menjadi:
@loggerDecorator("This is custom logger")
Perubahan kecil ini sebenarnya sangat penting karena membuat decorator jauh lebih fleksibel dan reusable.
1. Masalah yang ingin diselesaikan
Sebelumnya kita punya decorator seperti ini:
function logger(target: Function) {
console.log("logging");
}
Lalu digunakan:
@logger
class User {
// ...
}
Saat TypeScript menjalankan decorator tersebut, kira-kira:
@logger
β
logger(User)
Jadi logger() mendapatkan User sebagai target.
Masalahnya:
Bagaimana kalau kita ingin mengirim data tambahan ke logger?
Misalnya kita ingin menentukan sendiri pesan log:
@logger("This is custom logger")
class User {
// ...
}
Kita ingin "This is custom logger" masuk ke decorator.
Tetapi ada masalah.
2. Kenapa tidak langsung logger("...")?
Decorator biasa:
function logger(target: Function) {
console.log("logging");
}
digunakan:
@logger
class User {}
Perhatikan:
@logger
bukan:
@logger()
Karena logger sendiri adalah decorator function.
Decorator system-lah yang akan memanggilnya.
Secara konsep:
@logger
class User
β
TypeScript memanggil
logger(User)
Jadi kita tidak mengontrol pemanggilan logger() secara langsung.
Kalau kita ingin memberikan konfigurasi:
@logger("This is custom logger")
maka kita membutuhkan sebuah function tambahan yang tugasnya:
menerima parameter dari kita β kemudian menghasilkan decorator function.
Function inilah yang disebut Decorator Factory.
3. Apa itu Decorator Factory?
Definisi sederhananya:
Decorator Factory adalah function yang mengembalikan sebuah decorator function.
Atau:
Decorator Factory
β
menghasilkan
β
Decorator Function
Contohnya:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Di sini ada dua function.
Function pertama:
loggerDecorator()
adalah Decorator Factory.
Function kedua:
function logger(target: Function)
adalah Decorator.
4. Analogi sederhana
Bayangkan kamu punya mesin pembuat stiker.
Kamu datang ke mesin:
loggerDecorator("HELLO")
Mesin menerima:
"HELLO"
Kemudian mesin menghasilkan sebuah decorator:
logger
Jadi:
loggerDecorator("HELLO")
β
β menghasilkan
βΌ
logger decorator
Decorator yang dihasilkan tersebut kemudian ditempelkan ke class.
5. Struktur dasarnya
Bentuk umumnya:
function decoratorFactory(parameter) {
return function decorator(target) {
// decorator logic
};
}
Ada dua tahap:
Tahap 1 β Factory
decoratorFactory(parameter)
menerima konfigurasi.
Tahap 2 β Decorator
function decorator(target)
menerima target dari TypeScript.
Jadi:
parameter dari kita
β
decoratorFactory(parameter)
β
menghasilkan decorator
β
decorator(target)
Ini konsep paling penting dari lecture ini.
6. Contoh dari lecture
Kita buat:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Sekarang kita punya:
loggerDecorator
sebagai factory.
Sedangkan:
logger
yang ada di dalamnya adalah decorator.
7. Cara menggunakannya
Sekarang kita tidak lagi menulis:
@logger
Tetapi:
@loggerDecorator("This is custom logger")
Contoh lengkap:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
@loggerDecorator("This is custom logger")
class User {
constructor() {
console.log("User class Constructor called");
}
}
Nah, bagaimana prosesnya?
8. Mari bedah prosesnya
Ketika TypeScript menemukan:
@loggerDecorator("This is custom logger")
class User {}
pertama-tama:
loggerDecorator("This is custom logger")
dijalankan.
Factory menerima:
logMessage = "This is custom logger"
Kemudian factory mengembalikan:
function logger(target: Function) {
console.log(logMessage);
}
Decorator tersebut kemudian digunakan terhadap User.
Secara visual:
@loggerDecorator("This is custom logger")
β
βΌ
loggerDecorator(...)
β
β return
βΌ
function logger(target) { ... }
β
βΌ
diterapkan ke
β
βΌ
User
9. Kenapa ada dua function?
Ini bagian yang sering bikin bingung.
Kita punya:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Kenapa tidak cukup satu function?
Karena ada dua jenis informasi yang berbeda.
Informasi dari programmer
Misalnya:
"This is custom logger"
Ini kita berikan sendiri.
Informasi dari decorator system
Misalnya:
User
Ini diberikan oleh TypeScript ketika decorator diterapkan.
Jadi kita membutuhkan dua tahap:
TAHAP 1
Data dari programmer
"This is custom logger"
β
loggerDecorator(...)
β
TAHAP 2
Data dari TypeScript
User
β
logger(target)
10. logMessage dan target punya sumber berbeda
Ini sangat penting.
Perhatikan:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Ada:
logMessage
dan:
target
Mereka datang dari tempat berbeda.
logMessage
Datang dari kita:
@loggerDecorator("This is custom logger")
Sehingga:
logMessage
berisi:
"This is custom logger"
target
Datang dari decorator mechanism.
Karena decorator dipasang pada:
class User {}
maka:
target
merepresentasikan User.
Jadi:
logMessage
β
dari kita
target
β
dari TypeScript
11. Kenapa loggerDecorator harus dipanggil dengan ()?
Pada decorator biasa:
@logger
kita tidak menggunakan ().
Karena:
logger
langsung merupakan decorator.
Sedangkan pada decorator factory:
@loggerDecorator("Hello")
kita harus memanggil loggerDecorator.
Kenapa?
Karena loggerDecorator bukan decorator-nya.
Dia adalah pabrik yang menghasilkan decorator.
loggerDecorator
β
β dipanggil
βΌ
logger decorator
β
β diterapkan
βΌ
User
Jadi:
@logger
vs
@loggerDecorator("Hello")
berbeda.
12. Perbandingan paling penting
Decorator biasa
function logger(target: Function) {
console.log("logging");
}
@logger
class User {}
Flow:
@logger
β
logger(User)
Decorator Factory
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
@loggerDecorator("Hello")
class User {}
Flow:
@loggerDecorator("Hello")
β
loggerDecorator("Hello")
β
menghasilkan
β
logger
β
logger(User)
Jadi ada satu langkah ekstra.
13. Hubungannya dengan return
Ingat konsep JavaScript/TypeScript:
Function bisa mengembalikan function.
Contoh sederhana:
function createGreeting() {
return function greet() {
console.log("Hello");
};
}
Kita bisa melakukan:
const greeting = createGreeting();
Sekarang:
greeting
berisi function.
Sehingga:
greeting();
akan menjalankan:
Hello
Decorator Factory menggunakan konsep yang sama.
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Factory:
loggerDecorator()
menghasilkan function:
logger()
Dan function hasil tersebut digunakan sebagai decorator.
14. Bagian yang menarik: closure
Ada satu konsep JavaScript yang sebenarnya bekerja di balik contoh ini: closure.
Perhatikan:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
Function logger menggunakan:
logMessage
padahal logMessage adalah parameter milik function luar:
loggerDecorator
Contoh:
loggerDecorator("Hello")
menghasilkan decorator yang masih “mengingat”:
logMessage = "Hello"
Maka:
@loggerDecorator("Hello")
class User {}
akan membuat decorator yang tahu bahwa:
logMessage = "Hello"
Kalau kita membuat:
@loggerDecorator("User class created")
class User {}
maka decorator tersebut membawa:
"User class created"
15. Bisa punya banyak parameter
Lecture juga menyebutkan bahwa factory tidak harus menerima satu parameter.
Misalnya:
function loggerDecorator(
logMessage: string,
prefix: string
) {
return function logger(target: Function) {
console.log(prefix, logMessage);
};
}
Kemudian:
@loggerDecorator(
"User class initialized",
"[INFO]"
)
class User {}
Sekarang kita bisa mengirim berbagai konfigurasi.
Misalnya secara konsep:
loggerDecorator(
logMessage,
prefix,
...
)
Factory menjadi tempat untuk melakukan konfigurasi decorator.
16. Kenapa Decorator Factory berguna?
Bayangkan kita memiliki banyak class:
class User {}
class Product {}
class Order {}
Kita ingin masing-masing memiliki logging yang berbeda.
Dengan decorator biasa:
@logger
class User {}
@logger
class Product {}
@logger
class Order {}
semua menggunakan behavior yang sama.
Tetapi dengan factory:
@loggerDecorator("User class")
class User {}
@loggerDecorator("Product class")
class Product {}
@loggerDecorator("Order class")
class Order {}
Satu decorator factory bisa digunakan berkali-kali dengan konfigurasi berbeda.
Ini yang dimaksud lecture dengan:
lebih fleksibel dan reusable.
17. Urutan eksekusinya
Misalnya:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
@loggerDecorator("This is custom logger")
class User {
constructor() {
console.log("User class Constructor called");
}
}
Secara konsep:
Step 1
TypeScript melihat:
@loggerDecorator("This is custom logger")
Step 2
Factory dipanggil:
loggerDecorator("This is custom logger")
Step 3
Factory mengembalikan:
logger
Step 4
Decorator logger diterapkan ke User.
Secara konsep:
logger(User)
Step 5
Decorator menjalankan:
console.log(logMessage);
Output:
This is custom logger
Step 6
Kalau kita membuat instance:
const user = new User();
maka constructor berjalan:
User class Constructor called
Jadi output akhirnya:
This is custom logger
User class Constructor called
18. Factory bukan berarti langsung menjalankan constructor
Ini juga perlu dibedakan.
Ketika:
@loggerDecorator("Hello")
class User {}
diproses, factory/decorator bekerja terhadap class declaration.
Sedangkan:
new User();
membuat instance dan menjalankan constructor.
Jadi:
Decorator Factory
β
Decorator
β
Class
berbeda dengan:
new User()
β
Constructor
β
Instance
19. Analogi restoran π
Supaya lebih gampang.
Bayangkan:
loggerDecorator("Hello")
adalah kamu memesan burger dengan konfigurasi tertentu.
loggerDecorator = dapur/pabrik burger.
Parameter:
"Hello"
= pesanan/customization.
Kemudian factory menghasilkan:
burger yang sudah dikonfigurasi
Dalam decorator:
loggerDecorator("Hello")
β
menghasilkan
β
logger decorator
Jadi Factory membuat sesuatu, sedangkan Decorator adalah sesuatu yang dibuat tersebut.
20. Jangan tertukar dengan function biasa
Ada tiga hal berbeda:
1. Function biasa
function logger() {
console.log("logging");
}
Dipanggil:
logger();
2. Decorator
function logger(target: Function) {
console.log("logging");
}
Digunakan:
@logger
class User {}
3. Decorator Factory
function loggerDecorator(message: string) {
return function logger(target: Function) {
console.log(message);
};
}
Digunakan:
@loggerDecorator("Hello")
class User {}
Ringkasnya:
Function
β
dipanggil langsung
Decorator
β
diberikan ke @
Decorator Factory
β
dipanggil terlebih dahulu
β
menghasilkan Decorator
β
diberikan ke @
21. Pola yang harus kamu hafalkan
Kalau melihat:
function something(config) {
return function(target) {
// ...
};
}
kemungkinan besar kamu sedang melihat Decorator Factory.
Pola sederhananya:
function decoratorFactory(config) {
return function decorator(target) {
// decorator logic
};
}
Pemakaiannya:
@decoratorFactory(config)
class Something {}
22. Mental model paling sederhana
Ingat kalimat ini:
Decorator Factory = function yang membuat decorator.
Bukan:
decorator yang menerima parameter.
Secara konsep lebih tepat:
CONFIG
β
βΌ
ββββββββββββββββββββ
β Decorator Factoryβ
ββββββββββ¬ββββββββββ
β
β return
βΌ
ββββββββββββββββββββ
β Decorator β
ββββββββββ¬ββββββββββ
β
β target
βΌ
class User
Jadi ada factory di luar, decorator di dalam.
23. Hubungkan dengan lecture sebelumnya
Lecture sebelumnya:
function logger(target: Function) {
console.log("logging");
}
@logger
class User {}
Masalah:
Tidak ada tempat untuk mengirim konfigurasi
Lecture sekarang:
function loggerDecorator(logMessage: string) {
return function logger(target: Function) {
console.log(logMessage);
};
}
@loggerDecorator("This is custom logger")
class User {}
Sekarang:
konfigurasi
β
@loggerDecorator("This is custom logger")
β
decorator dibuat
β
logger(target)
β
User class
Jadi Decorator Factory menyelesaikan masalah bagaimana memberikan konfigurasi dari luar kepada decorator.
24. @logger vs @loggerDecorator(...)
Ini wajib benar-benar paham.
| Syntax | Artinya |
|---|---|
@logger | logger adalah decorator |
@logger() | memanggil function untuk menghasilkan decorator |
@loggerDecorator("Hello") | memanggil decorator factory dengan konfigurasi |
logger(target) | decorator menerima target |
Contoh:
@logger
class User {}
langsung menggunakan:
logger
Sedangkan:
@loggerDecorator("Hello")
class User {}
melakukan:
loggerDecorator("Hello")
β
return logger
β
gunakan logger sebagai decorator
25. Kesimpulan lecture
Ada beberapa poin yang sebaiknya kamu ingat.
1. Decorator
Decorator adalah function yang diterapkan menggunakan @.
@logger
class User {}
2. Decorator Factory
Decorator Factory adalah function yang menghasilkan decorator.
function loggerDecorator(message: string) {
return function logger(target: Function) {
console.log(message);
};
}
3. Factory menerima konfigurasi
@loggerDecorator("This is custom logger")
"This is custom logger" berasal dari kita.
4. Decorator menerima target
function logger(target: Function)
target diberikan oleh mekanisme decorator dan dalam contoh lecture ini merepresentasikan class User.
5. Kenapa menggunakan Factory?
Karena kita ingin decorator bisa dikonfigurasi:
@loggerDecorator("User")
class User {}
@loggerDecorator("Product")
class Product {}
@loggerDecorator("Order")
class Order {}
Satu factory β bisa menghasilkan decorator dengan konfigurasi berbeda.
π§ Cheat Sheet
Decorator:
function logger(target) { ... }
@logger
class User {}
β
Decorator Factory:
function loggerDecorator(config) {
return function logger(target) {
// ...
};
}
β
Penggunaan:
@loggerDecorator(config)
class User {}
β
Flow:
config dari programmer
β
loggerDecorator(config)
β
return decorator
β
decorator(target)
β
User
Kalau kamu hanya mengingat satu kalimat dari lecture ini, ingat:
Decorator Factory adalah function yang menerima konfigurasi dari luar dan mengembalikan decorator function yang nantinya diterapkan ke class/method/property/etc.