Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط Singleton

جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 86 من 107.

يضمن نمط Singleton أن يكون للنوع مثيل واحد فقط طوال برنامجك، ويوفّر نقطة وصول عامة إليه. وهذا مفيد للموارد المشتركة مثل مديري الإعدادات، واتصالات قواعد البيانات، أو المسجّلات.

في Go، نطبّق Singleton باستخدام متغيّر على مستوى package مع sync.Once لضمان تهيئة آمنة للخيوط:

package config

import "sync"

type Config struct {
    DatabaseURL string
    MaxRetries  int
}

var (
    instance *Config
    once     sync.Once
)

func GetInstance() *Config {
    once.Do(func() {
        instance = &Config{
            DatabaseURL: "localhost:5432",
            MaxRetries:  3,
        }
    })
    return instance
}

يضمن sync.Once أن تعمل وظيفة التهيئة once بالضبط، حتى عندما تستدعي عدة goroutines الدالة GetInstance() في الوقت نفسه. يعيد كل استدعاء لاحق نفس instance دون إعادة تنفيذ كود التهيئة.

cfg1 := config.GetInstance()
cfg2 := config.GetInstance()
// cfg1 و cfg2 يشيران إلى نفس المثيل

استخدم Singleton باعتدال. ورغم أنه مريح، فإنه يُدخل حالة عامة قد تجعل الاختبار أكثر صعوبة وتخفي التبعيات. فكّر فيما إذا كان حقن التبعيات قد يكون بديلاً أفضل لحالة الاستخدام الخاصة بك.

challenge icon

التحدي

سهل

لنبنِ Logger singleton يضمن وجود مثيل Logger واحد فقط طوال تطبيقك! هذه حالة استخدام كلاسيكية لنمط Singleton. تريد أن تشارك جميع أجزاء برنامجك إعدادات Logger وحالته نفسهما.

ستنظّم شيفرتك عبر ملفين:

  • logger.go: Implement Logger Singleton آمنًا للخيوط.

    أنشئ بنية Logger بحقلين: Prefix (من النوع string) لبادئات رسائل السجل، وMessageCount (من النوع int) لتتبّع عدد الرسائل التي تم تسجيلها.

    استخدم متغيرات على مستوى الحزمة مع sync.Once لضمان إنشاء مثيل واحد فقط على الإطلاق. Implement دالة GetLogger() التي تهيّئ Logger ببادئة افتراضية هي "[LOG]" وتعيد المثيل الوحيد.

    أضف هذه الأساليب إلى Logger:

    • SetPrefix(prefix string) - يغيّر بادئة Logger
    • Log(message string) string - يزيد عدد الرسائل ويعيد سلسلة منسّقة: [prefix] #[count]: [message]
    • GetCount() int - يعيد إجمالي عدد الرسائل التي تم تسجيلها
  • main.go: Demonstrate أن الاستدعاءات المتعددة لـ GetLogger() تعيد المثيل نفسه.

    اقرأ قيمة بادئة جديدة، ثم اقرأ عدد الرسائل متبوعًا بكل رسالة لتسجيلها.

    احصل على مثيل Logger، واضبط البادئة المخصّصة، ثم سجّل كل رسالة واطبع النتيجة. بعد تسجيل جميع الرسائل، احصل على مثيل Logger مرة أخرى (لإثبات أنه المثيل نفسه) واطبع إجمالي عدد الرسائل.

ستُ 제공 المدخلات التالية:

  • السطر 1: البادئة المخصّصة المطلوب ضبطها
  • السطر 2: عدد الرسائل
  • الأسطر التالية: كل رسالة مطلوب تسجيلها

على سبيل المثال، عند إعطاء:

[APP]
3
Server started
User connected
Request processed

يجب أن يكون ناتجك:

[APP] #1: Server started
[APP] #2: User connected
[APP] #3: Request processed
Total messages logged: 3

وعند إعطاء:

[DEBUG]
2
Initializing cache
Cache ready

يجب أن يكون ناتجك:

[DEBUG] #1: Initializing cache
[DEBUG] #2: Cache ready
Total messages logged: 2

وعند إعطاء:

[ERROR]
1
Connection failed

يجب أن يكون ناتجك:

[ERROR] #1: Connection failed
Total messages logged: 1

الفكرة الأساسية هي أنه بغض النظر عن عدد مرات استدعائك لـ GetLogger()، ستحصل دائمًا على المثيل نفسه بحالته المشتركة. يستمر عدد الرسائل لأن هناك Logger واحدًا فقط موجودًا!

جرّب بنفسك

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// اقرأ البادئة المخصصة
	prefix, _ := reader.ReadString('\n')
	prefix = strings.TrimSpace(prefix)

	// اقرأ عدد الرسائل
	countStr, _ := reader.ReadString('\n')
	countStr = strings.TrimSpace(countStr)
	count, _ := strconv.Atoi(countStr)

	// اقرأ كل رسالة
	messages := make([]string, count)
	for i := 0; i < count; i++ {
		msg, _ := reader.ReadString('\n')
		messages[i] = strings.TrimSpace(msg)
	}

	// TODO: احصل على مثيل المُسجِّل باستخدام GetLogger()

	// TODO: عيِّن البادئة المخصصة باستخدام SetPrefix()

	// TODO: سجِّل كل رسالة واطبع النتيجة

	// TODO: احصل على مثيل المُسجِّل مرة أخرى (لإثبات أنه نفس المثيل)
	// and print the total message count in format: "Total messages logged: X"
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Go عبر الإنترنت