Decorator Pattern #
Dalam pengembangan software, sering kali kita ingin menambahkan perilaku (behavior) baru ke sebuah objek tanpa mengubah kode aslinya. Jika menggunakan inheritance, solusi ini cepat menjadi kompleks dan kaku. Di sinilah Decorator Pattern berperan sebagai solusi yang lebih fleksibel dan scalable.
Decorator Pattern termasuk dalam Structural Design Pattern, dan sangat berguna ketika sistem perlu berkembang secara bertahap tanpa merusak struktur yang sudah stabil.
Apa itu Decorator Pattern? #
Decorator Pattern adalah pola desain yang memungkinkan kita menambahkan fitur atau behavior tambahan ke sebuah objek secara dinamis, dengan cara membungkus (wrap) objek tersebut menggunakan objek lain yang memiliki interface yang sama.
Alih-alih membuat banyak subclass untuk setiap kombinasi fitur, decorator menggunakan composition over inheritance.
Secara konsep:
- Ada Component (interface)
- Ada ConcreteComponent (implementasi dasar)
- Ada Decorator (wrapper yang juga mengimplementasikan interface)
- Ada ConcreteDecorator (implementasi decorator dengan behavior tambahan)
Tujuan Pattern #
Decorator Pattern digunakan untuk:
- Menambahkan behavior tanpa memodifikasi kode existing (Open/Closed Principle)
- Menghindari ledakan subclass akibat kombinasi fitur
- Menyusun behavior secara fleksibel dan modular
- Mendukung runtime composition (fitur bisa ditambah/dikurangi saat runtime)
Kapan Cocok Digunakan? #
Decorator Pattern sangat cocok digunakan ketika:
- Kamu ingin menambahkan fitur secara opsional
- Fitur bisa dikombinasikan dalam berbagai variasi
- Inheritance mulai terasa kaku dan sulit dirawat
- Kamu ingin mengikuti prinsip SOLID, terutama OCP
Contoh use case umum:
- Logging (request logger, audit logger)
- Middleware (HTTP, RPC)
- Compression / encryption stream
- Metrics dan monitoring
- Authorization / authentication layer
Perbandingan dengan Inheritance #
| Inheritance | Decorator |
|---|---|
| Static (compile-time) | Dynamic (runtime) |
| Banyak subclass | Kombinasi fleksibel |
| Kaku | Fleksibel |
| Sulit di-extend | Mudah ditambah |
Contoh Implementasi (Golang) #
Studi Kasus: Notifier #
Kita ingin membuat sistem notifikasi dasar, lalu menambahkan fitur seperti logging dan metrics.
Component Interface #
type Notifier interface {
Send(message string)
}
Concrete Component #
type EmailNotifier struct {}
func (e *EmailNotifier) Send(message string) {
fmt.Println("Sending email:", message)
}
Base Decorator #
type NotifierDecorator struct {
wrapped Notifier
}
func (d *NotifierDecorator) Send(message string) {
d.wrapped.Send(message)
}
Concrete Decorator: Logging #
type LoggingDecorator struct {
NotifierDecorator
}
func NewLoggingDecorator(n Notifier) Notifier {
return &LoggingDecorator{
NotifierDecorator{wrapped: n},
}
}
func (l *LoggingDecorator) Send(message string) {
fmt.Println("[LOG] Sending notification")
l.wrapped.Send(message)
}
Concrete Decorator: Metrics #
type MetricsDecorator struct {
NotifierDecorator
}
func NewMetricsDecorator(n Notifier) Notifier {
return &MetricsDecorator{
NotifierDecorator{wrapped: n},
}
}
func (m *MetricsDecorator) Send(message string) {
start := time.Now()
m.wrapped.Send(message)
fmt.Println("[METRICS] duration:", time.Since(start))
}
Penggunaan #
func main() {
var notifier Notifier = &EmailNotifier{}
notifier = NewLoggingDecorator(notifier)
notifier = NewMetricsDecorator(notifier)
notifier.Send("Hello Decorator")
}
Output akan menunjukkan behavior yang tersusun secara berlapis.
Decorator Pattern vs Middleware #
Decorator Pattern sangat mirip dengan konsep middleware:
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("incoming request")
next.ServeHTTP(w, r)
})
}
Ini adalah contoh decorator dalam bentuk fungsional.
Kesalahan Umum #
- Terlalu banyak decorator kecil → sulit dibaca
- Dependency tersembunyi antar decorator
- Debugging jadi sulit jika layering terlalu dalam
Best Practices #
1. Gunakan Interface yang Kecil dan Fokus #
Interface yang terlalu besar akan membuat decorator sulit diimplementasikan.
2. Hindari Logic Berat di Base Decorator #
Base decorator sebaiknya hanya meneruskan call, tanpa behavior tambahan.
3. Urutan Decorator Itu Penting #
Urutan pembungkusan bisa mempengaruhi hasil:
- Logging sebelum metrics ≠ metrics sebelum logging
4. Jangan Overuse #
Jika hanya ada 1 variasi behavior, decorator bisa menjadi overengineering.
5. Manfaatkan Functional Decorator (Go-style) #
Di Golang, decorator sering diimplementasikan dengan function wrapping, terutama pada HTTP middleware.
Penutup #
Decorator Pattern adalah solusi elegan untuk menambahkan behavior secara dinamis tanpa mengubah kode existing. Dengan memanfaatkan composition dan interface, decorator membantu menjaga kode tetap fleksibel, scalable, dan sesuai dengan prinsip SOLID.
Dalam ekosistem Golang, pattern ini sangat alami digunakan, baik dalam bentuk struct-based maupun function-based decorator.