🎨 دیزاین پترن Decorator — افزودن قابلیت بدون تغییر در کد اصلی
مشکل کجاست؟
فرض کن یه سرویس لاگگیر (Logger) داری که سادهترین کارش نوشتن متن لاگ توی کنسوله. حالا مشتری میاد میگه:
میخوام لاگ توی فایل هم ذخیره بشه.
بعد یکی دیگه میگه: میخوام لاگهام روی دیتابیس برن.
یا حتی بدتر: همزمان روی کنسول + فایل + دیتابیس ثبت بشه.
راه سنتی؟
اینه که برای هر حالت یه کلاس جدید بنویسی.
- ConsoleLogger
- FileLogger
- DBLogger
- ConsoleAndFileLogger
- FileAndDBLogger
و همینطور الی آخر... 😵
نتیجه؟ انفجار کلاسها و کدی که قابل نگهداری نیست.
راه حل: Decorator 🎭
به جای این همه کلاس، میایم یه اینترفیس Logger تعریف میکنیم و هر چیزی که لاگگیری انجام بده باید این قرارداد رو پیادهسازی کنه. بعدش دکوریتورها میان وسط: هر کدوم از این دکوریتورها یه لایه جدید به لاگ اضافه میکنن.
package main
import "fmt"
type Logger interface {
Log(message string)
}
type ConsoleLogger struct{}
func (c *ConsoleLogger) Log(message string) {
fmt.Println("Console:", message)
}
type FileLogger struct {
Wrapped Logger
}
func (f *FileLogger) Log(message string) {
f.Wrapped.Log(message)
fmt.Println("File:", message)
}
type DBLogger struct {
Wrapped Logger
}
func (d *DBLogger) Log(message string) {
d.Wrapped.Log(message)
fmt.Println("Database:", message)
}
func main() {
console := &ConsoleLogger{}
consoleFile := &FileLogger{Wrapped: console}
fullLogger := &DBLogger{Wrapped: consoleFile}
fullLogger.Log("Decorator Pattern is awesome!")
}
خروجی:
Console: Decorator Pattern is awesome!
File: Decorator Pattern is awesome!
Database: Decorator Pattern is awesome!
چرا این روش خفنتره؟
- میتونی ترکیب دلخواه بسازی، بدون نیاز به کلاسهای جدید.
- اصل Open/Closed Principle رو رعایت میکنه: کد باز برای توسعه، بسته برای تغییر.
- هر دکوریتور میتونه فقط مسئول کار خودش باشه (اصل Single Responsibility).
- کد تمیز، انعطافپذیر و قابل نگهداری میشه.
جمعبندی
Decorator یه پترن ساده ولی فوقالعاده قدرتمنده. بهت اجازه میده قابلیتهای جدید رو مثل ماژولهای LEGO روی سیستم سوار کنی، بدون اینکه کد اصلی رو دست بزنی یا کلاسهای بیپایان درست کنی.