Facade Pattern الگوی طراحی ساختاری
🎯 اهداف اصلی Facade Pattern:
- مخفی کردن پیچیدگیها برای بهبود خوانایی کد
- کاهش coupling (وابستگی شدید) بین کد کلاینت و زیرسیستمها
مثل پذیرش هتل فکر کنید: به جای اینکه جداگانه با آشپز، نظافتچی و مدیر تماس بگیرید، فقط به پذیرش مراجعه میکنید.
از Facade Pattern میتوان استفاده کرد وقتی:
- زیرسیستم پیچیدهای با وابستگیهای زیاد دارید.
- میخواهید کدتان را از جزئیات زیرسیستم جدا کنید.
میخواهید یک نقطه ورود (Entry Point) تمیز و ساده برای کد کلاینت فراهم کنید.
✨ Facade vs Service
خیلی وقتها Facade با Service اشتباه گرفته میشود
Facade Pattern: پیچیدگیهای زیرسیستم را مخفی میکند.
Service Pattern: منطق تجاری (Business Logic) را کپسوله میکند.
مثلاً:
Facade میتواند ذخیرهسازی روی دیسک را مدیریت کند.
Service میتواند محاسبه حقوق را مدیریت کند.
🎯 تشبیه ساده
فرض کنید توی یک هتل هستید. شما برای هر نیازی (غذا، نظافت، تاکسی) مستقیم نمیرید سراغ هر مسئول. فقط به پذیرش زنگ میزنید، اونها همه چیز رو هماهنگ میکنن.
این دقیقاً کاریه که Facade میکنه: پیچیدگی رو پشت پرده مخفی میکنه و یک ورودی ساده به شما میده.
🚀 مثال در Go
فرض کنیم میخوایم یک سیستم ساده برای سفارش قهوه درست کنیم. چند بخش مختلف داریم:
آسیاب (Grinder)
آبجوشکن (WaterHeater)
قهوهساز (CoffeeMachine)
هرکدوم وظیفه خودش رو دارن.
بدون Facade چطور پیاده سازی میشه ؟
package main
import "fmt"
type CoffeeMachine struct{}
func (c *CoffeeMachine) Start() { fmt.Println("Coffee machine started") }
func (c *CoffeeMachine) Brew() { fmt.Println("Brewing coffee") }
type Grinder struct{}
func (g *Grinder) GrindBeans() { fmt.Println("Grinding coffee beans") }
type WaterHeater struct{}
func (w *WaterHeater) Heat() { fmt.Println("Heating water") }
func main() {
coffee := &CoffeeMachine{}
grinder := &Grinder{}
heater := &WaterHeater{}
coffee.Start()
grinder.GrindBeans()
heater.Heat()
coffee.Brew()
fmt.Println("Coffee is ready!")
}
اینجا کلاینت باید خودش تمام مراحل رو مدیریت کنه → وابستگی بالا ❌
با Facade:
package main
import "fmt"
type CoffeeMachine struct{}
func (c *CoffeeMachine) Start() { fmt.Println("Coffee machine started") }
func (c *CoffeeMachine) Brew() { fmt.Println("Brewing coffee") }
type Grinder struct{}
func (g *Grinder) GrindBeans() { fmt.Println("Grinding coffee beans") }
type WaterHeater struct{}
func (w *WaterHeater) Heat() { fmt.Println("Heating water") }
type CoffeeFacade struct {
machine *CoffeeMachine
grinder *Grinder
heater *WaterHeater
}
func NewCoffeeFacade(m *CoffeeMachine, g *Grinder, h *WaterHeater) *CoffeeFacade {
return &CoffeeFacade{machine: m, grinder: g, heater: h}
}
func (f *CoffeeFacade) MakeCoffee() {
f.machine.Start()
f.grinder.GrindBeans()
f.heater.Heat()
f.machine.Brew()
fmt.Println("Coffee is ready! ☕")
}
func main() {
facade := NewCoffeeFacade(&CoffeeMachine{}, &Grinder{}, &WaterHeater{})
facade.MakeCoffee()
}
✅ مزایا
- کد کلاینت ساده و تمیز میشه → فقط facade.MakeCoffee()
- وابستگیها کاهش پیدا میکنه.
- تغییر در زیرسیستم فقط توی Facade هندل میشه، نه توی همه جای برنامه.
- اصل Single Responsibility و Open/Closed Principle رعایت میشه.
🎬 جمعبندی
Facade Pattern بهت کمک میکنه پیچیدگی زیرسیستمها رو پشت یک رابط ساده مخفی کنی.
این الگو مثل همون پذیرش هتل عمل میکنه: یک در ورودی برای همه نیازها.
اگه میخوای کدت تمیز، قابل نگهداری و توسعهپذیر باشه، وقتشه به Facade یه شانس بدی. 😉