أنماط التصميم هي حلول متكررة للمشاكل الشائعة في تصميم البرمجيات. يصنّف "فريق الأربعة" (GoF) هذه الأنماط إلى ثلاثة أنواع رئيسية: الإنشائية، والهيكلية، والسلوكية. في هذا المنشور، نستكشف تطبيقات عملية باستخدام لغة Go، مع التركيز على الوضوح، والبساطة، والأكواد المتعارف عليها. سواء كنت تبني خدمات مصغرة (Microservices) أو أنظمة أحادية معقدة (Monoliths)، فإن فهم هذه الأنماط يساعدك على كتابة أكواد قابلة للصيانة، والقابلة للتوسع، وقابلة للاختبار.
الأنماط الإنشائية: إنشاء الكائنات
تعنى الأنماط الإنشائية بآليات إنشاء الكائنات، وتهدف إلى إنشاء الكائنات بطريقة مناسبة للوضع. الأكثر شيوعاً هو طريقة المصنع (Factory Method).
طريقة المصنع في Go
في لغة Go، تعتبر طرق المصنع القائمة على الواجهات (Interfaces) متعارفاً عليها. فكّر في نظام دفع يدعم معالجات متعددة:
type PaymentProcessor interface {
Process(amount float64) error
}
type StripeProcessor struct{}
func (s StripeProcessor) Process(amount float64) error {
fmt.Printf("Processing $%.2f via Stripe\n", amount)
return nil
}
type PayPalProcessor struct{}
func (p PayPalProcessor) Process(amount float64) error {
fmt.Printf("Processing $%.2f via PayPal\n", amount)
return nil
}
func NewPaymentProcessor(provider string) PaymentProcessor {
switch provider {
case "stripe":
return StripeProcessor{}
case "paypal":
return PayPalProcessor{}
default:
return nil
}
}
يفصل هذا الكود العميل (Client Code) عن التنفيذيات الملموسة. يمكنك بسهولة إضافة طرق دفع جديدة دون تعديل الكود الموجود، مما يتوافق مع مبدأ مفتوح/مغلق (Open/Closed Principle).
الأنماط الهيكلية: تركيب الكائنات
تعنى الأنماط الهيكلية بكيفية تركيب الفئات والكائنات لتكوين هياكل أكبر. يُعد نمط المحوّل (Adapter Pattern) حاسماً لدمج الواجهات غير المتوافقة.
نمط المحوّل في Go
لنفترض أن لديك واجهة تسجيل (Logging) قديمة تختلف عن مسجّل (Logger) تطبيقك الحديث:
type LegacyLogger interface {
Log(msg string)
}
type AppLogger interface {
Info(msg string)
Error(msg string)
}
type LoggerAdapter struct {
legacy LegacyLogger
}
func (a LoggerAdapter) Info(msg string) {
a.legacy.Log("[INFO] " + msg)
}
func (a LoggerAdapter) Error(msg string) {
a.legacy.Log("[ERROR] " + msg)
}
func NewLoggerAdapter(legacy LegacyLogger) AppLogger {
return LoggerAdapter{legacy: legacy}
}
يسمح هذا المحوّل لتطبيقك الحديث باستخدام المسجّل القديم دون تغيير واجهته، مما يعزز إعادة استخدام الكود وإعادة الهيكلة التدريجية.
الأنماط السلوكية: تفاعل الكائنات
تتعلق الأنماط السلوكية بالخوارزميات وتوزيع المسؤوليات بين الكائنات. يُعد نمط المراقب (Observer Pattern) ضرورياً للبنى المعمارية القائمة على الأحداث (Event-driven).
نمط المراقب في Go
توفر قنوات Go (Channels) تنفيذاً طبيعياً لنمط المراقب للتواصل غير المتزامن:
type Event struct {
ID int
Data string
}
type EventHandler func(Event)
type EventBus struct {
handlers map[string][]EventHandler
}
func NewEventBus() *EventBus {
return &EventBus{handlers: make(map[string][]EventHandler)}
}
func (eb *EventBus) Subscribe(eventType string, handler EventHandler) {
eb.handlers[eventType] = append(eb.handlers[eventType], handler)
}
func (eb *EventBus) Publish(event Event) {
for _, handler := range eb.handlers["order"] {
go handler(event)
}
}
func main() {
bus := NewEventBus()
bus.Subscribe("order", func(e Event) {
fmt.Printf("Email sent for order %d\n", e.ID)
})
bus.Subscribe("order", func(e Event) {
fmt.Printf("Notification sent for order %d\n", e.ID)
})
bus.Publish(Event{ID: 1, Data: "New Order"})
}
يسمح هذا الفصل الضعيف (Loose Coupling) لعدة مشتركين بالتفاعل مع الأحداث دون معرفة بعضهم البعض، وهو مثالي للخدمات المصغرة والأنظمة القائمة على الأحداث.
الخاتمة
أنماط التصميم ليست حلولاً تناسب الجميع، بل هي قوالب مجربة تعالج التحديات المعمارية الشائعة. تبسّط الأنماط الإنشائية إنشاء الكائنات، وتدير الأنماط الهيكلية العلاقات بين الكائنات، بينما تتعامل الأنماط السلوكية مع التواصل. في لغة Go، استغل نقاط قوة اللغة مثل الواجهات والقنوات لتنفيذ هذه الأنماط بشكل متعارف عليه. تذكّر: اختر الأنماط التي تحل مشكلتك المحددة، ولا تفرضها في أماكن لا تناسبها. سيؤدي إتقان هذه الأنماط إلى رفع مستوى كودك من مجرد وظيفي إلى قوي وقابل للصيانة.