Go Programming

گو ایڈیوماتک: مقیاس پذیر نظاموں کے لیے ڈیزائن پیٹرنز کی پیاده سازی

گو، یا گو لانگ، تیزی سے جدید کلاؤڈ نیٹو انفراسٹرکچر کی ریڑھ کی ہڈی بن گیا ہے۔ اس کی سادگی، کنکرینسی ماڈل اور وضاحت ان ڈویلپرز کو متاثر کرتی ہے جو کارکردگی کی قدر کرتے ہیں۔ تاہم، گو منتقل ہونے والے ڈویلپرز میں ایک عام غلط فہمی یہ ہے کہ انہیں جاوا یا سی++ جیسی زبانوں میں پائے جانے والے ڈیزائن پیٹرنز سے سختی سے گریز کرنا چاہیے۔ یہ غلط ہے۔ گو ڈیزائن پیٹرنز کو اپناتا ہے لیکن زیادہ مینیملسٹ اور ایڈیوماتک نقطہ نظر کو ترجیح دیتا ہے۔ اس پوسٹ میں، ہم تین کلاسیکی پیٹرنز—سنگلٹن، فیکٹری، اور اوبزروور—کی پیاده سازی کا جائزہ لیں گے، ایسے طریقے سے جو گو ایکو سسٹم کے لیے مقامی محسوس ہو۔

گو فلسفہ: پیچیدگی پر سادگی

کوڈ میں گہرائی میں جانے سے پہلے، یہ سمجھنا ضروری ہے کہ گو ورثہ (inheritance) کے مقابلے میں کمپوزیشن (composition) کی ترغیب دیتا ہے۔ روایتی گو ایف (گینگ آف فور) پیٹرنز اکثر گہرے ورثہ کے ہائرارکیز پر بہت زیادہ انحصار کرتے ہیں۔ گو میں، ہم چھوٹی، واحد مقصد والی انٹرفیسز کو کمپوز کر کے ساختی مسائل کو حل کرتے ہیں۔ یہ کپلنگ کو کم کرتا ہے اور ٹیسٹنگ کو نمایاں طور پر آسان بناتا ہے۔ اگرچہ آپ کو ایک پیچیدہ ایبسٹریکٹ کلاس کی ضرورت نہ ہو، لیکن آپ برتاؤ کو بیان کرنے کے لیے انٹرفیسز اور حالت (state) کو برقرار رکھنے کے لیے اسٹرکچرز کا بار بار استعمال کریں گے۔

1. سنگلٹن پیٹرن: محفوظ اور ایڈیوماتک

سنگلٹن پیٹرن یہ یقینی بناتا ہے کہ ایک کلاس کا صرف ایک انسٹنس ہو اور اس تک رسائی کا ایک عالمی نقطہ فراہم کرے۔ لیزی انیشلائزیشن (lazy initialization) والی زبانوں میں، تھریڈ سیفٹی ایک بڑا مسئلہ ہے۔ گو میں، سنگلٹنز کو ہینڈل کرنے کا ایڈیوماتک طریقہ sync پیکج کا استعمال ہے، خاص طور پر sync.Once، جو یہ یقینی بناتا ہے کہ انیشلائزیشن صرف ایک بار ہوتی ہے، حتیٰ کہ کنکرینٹ ماحول میں بھی۔

package singleton

import "sync"

// DatabaseConn ایک واحد ڈیٹا بیس کنکشن کی نمائندگی کرتا ہے۔
type DatabaseConn struct {
    Host string
    Port int
}

var (
    instance *DatabaseConn
    once     sync.Once
)

// GetInstance DatabaseConn کا واحد انسٹنس واپس کرتا ہے۔
func GetInstance() *DatabaseConn {
    once.Do(func() {
        instance = &DatabaseConn{
            Host: "localhost",
            Port: 5432,
        }
    })
    return instance
}

sync.Once کا استعمال کر کے، ہم مینوئل موٹیکس لاکنگ اور انلاک چیکز کے بولیئر پلیٹ سے بچتے ہیں۔ یہ عالمی حالت (global state) کی لیزی انیشلائزیشن کے لیے اسٹینڈرڈ لائبریری کا پسندیدہ طریقہ ہے۔

2. فیکٹری پیٹرن: تخلیقی منطق کو انتزاعی بنانا

فیکٹری پیٹرنز کا استعمال اشیاء کو بنانے کے لیے کیا جاتا ہے بغیر اس بات کی وضاحت کے کہ بننے والی اشیاء کی درست کلاس کیا ہے۔ گو میں، اسے اکثر ایسا فنکشنز یا طریقوں کے ذریعے نافذ کیا جاتا ہے جو ایک انٹرفیس واپس کرتے ہیں۔ یہ آپ کو کنزیومر کوڈ کو تبدیل کیے بغیر نفاذات کو تبدیل کرنے کی اجازت دیتا ہے، جو ڈیپنڈنسی انجیکشن اور ٹیسٹنگ کے لیے انتہائی اہم ہے۔

package notification

// Notifier وہ انٹرفیس ہے جو پیغامات بھیجنے کے معاہدے کو بیان کرتا ہے۔
type Notifier interface {
    Send(message string) error
}

// EmailService Notifier کو نافذ کرتا ہے۔
type EmailService struct{}

func (e *EmailService) Send(message string) error {
    // ای میل بھیجنے کی منطق
    return nil
}

// SMSService Notifier کو نافذ کرتا ہے۔
type SMSService struct{}

func (s *SMSService) Send(message string) error {
    // ایس ایم ایس بھیجنے کی منطق
    return nil
}

// NewNotifier فراہم کردہ قسم کے مطابق ایک Notifier بناتا ہے۔
func NewNotifier(serviceType string) (Notifier, error) {
    switch serviceType {
    case "email":
        return &EmailService{}, nil
    case "sms":
        return &SMSService{}, nil
    default:
        return nil, fmt.Errorf("unsupported notifier type: %s", serviceType)
    }
}

یہ نقطہ نظر تخلیقی منطق کو مرکزی بناتا ہے اور ایپلیکیشن کے باقی حصے کو کانکریٹ اسٹرکچرز کے بجائے Notifier انٹرفیس پر انحصار کرنے کی اجازت دیتا ہے۔

3. اوبزروور پیٹرن: کنکرینسی کو آسان بنانا

جبکہ گو میں جے ایس جی جیے بلٹ ان ایونٹ لسٹنرز نہیں ہیں، ہم گوروٹینز اور چینلز کا استعمال کرتے ہوئے اوبزروور پیٹرن کو نافذ کر سکتے ہیں۔ یہ سروسز کو ڈی کپل کرنے کے لیے خاص طور پر مفید ہے، جیسے کہ سسٹم ایونٹس لاگ کرنا یا منسلک کلائنٹس کو اپڈیٹس براڈکاسٹ کرنا۔

package observer

type EventBus struct {
    subscribers map[string][]chan string
}

func NewEventBus() *EventBus {
    return &EventBus{
        subscribers: make(map[string][]chan string),
    }
}

func (eb *EventBus) Subscribe(event string, ch chan string) {
    eb.subscribers[event] = append(eb.subscribers[event], ch)
}

func (eb *EventBus) Publish(event string, message string) {
    if chs, ok := eb.subscribers[event]; ok {
        for _, ch := range chs {
            go func(c chan string) {
                c <- message
            }(ch)
        }
    }
}

ہر سبسکرائبر کے لیے ایک گوروٹن لانچ کر کے، ہم یقینی بناتے ہیں کہ اگر کوئی سبسکرائبر سست ہو تو پبلشر بلاک نہیں ہوتا۔ یہ دوسری زبانوں میں روایتی طور پر زیادہ پیچیدہ ہونے والے ایک پیٹرن کو نافذ کرنے کے لیے گو کی کنکرینسی پریمیٹیوز کا استعمال کرتا ہے۔

نتیجہ

گو میں ڈیزائن پیٹرنز کی پیاده سازی سخت ٹیمپلیٹس کی پیروی کرنے کے بارے میں کم ہے اور زبان کی طاقتوں—انٹرفیسز، کمپوزیشن، اور کنکرینسی—کو استعمال کرنے کے بارے میں زیادہ ہے۔ سنگلٹنز کے لیے sync.Once، فیکٹریز کے لیے انٹرفیسز واپس کرنے والے فنکشنز، اور اوبزروورز کے لیے چینلز کا استعمال کر کے، آپ ایسا کوڈ لکھتے ہیں جو نہ صرف فنکشنل بلکہ ایڈیوماتک اور برقرار رکھنے میں آسان بھی ہے۔ یاد رکھیں، بہترین گو کوڈ سادہ، واضح ہوتا ہے اور جہاں ممکن ہو اسٹینڈرڈ لائبریری کا استعمال کرتا ہے۔

Share: