Menu
Coddy logo textTech

תבנית Singleton

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 86 מתוך 107.

תבנית Singleton מבטיחה שלטיפוס תהיה רק מופע אחד לאורך כל התוכנית שלך, ומספקת נקודת גישה גלובלית אליו. היא שימושית למשאבים משותפים כמו מנהלי תצורה, חיבורי מסד נתונים או רושמי יומן.

ב-Go, אנחנו מממשים Singleton באמצעות משתנה ברמת החבילה בשילוב עם 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 מבטיח שפונקציית האתחול תרוץ בדיוק פעם אחת, גם כשכמה goroutines קוראות ל־GetInstance() בו־זמנית. כל קריאה נוספת מחזירה את אותו מופע בלי להריץ מחדש את קוד האתחול.

cfg1 := config.GetInstance()
cfg2 := config.GetInstance()
// cfg1 ו-cfg2 מצביעים על אותו מופע

השתמשו ב-Singleton במשורה. אף שהוא נוח, הוא מוסיף מצב גלובלי שעלול להקשות על בדיקות ולהסתיר תלויות. שקלו אם הזרקת תלויות עשויה להיות חלופה טובה יותר עבור המקרה שלכם.

challenge icon

אתגר

קל

בואו נבנה Logger יחידני שמבטיח שקיימת רק מופע אחד של רכיב הרישום באפליקציה שלכם! זהו מקרה שימוש קלאסי לדפוס Singleton. רוצים שכל חלקי התוכנית שלכם ישתפו את אותה תצורה ואת אותו מצב של רכיב הרישום.

תארגנו את הקוד שלכם בשני קבצים:

  • logger.go: ממשו רכיב רישום יחידני בטוח לשימוש במקביל.

    צרו מבנה Logger עם שני שדות: Prefix (מחרוזת) עבור קידומות של הודעות הרישום, ו-MessageCount (int) כדי לעקוב אחר מספר ההודעות שנרשמו.

    השתמשו במשתנים ברמת החבילה עם sync.Once כדי להבטיח שנוצר רק מופע אחד. ממשו פונקציית GetLogger() שמאתחלת את רכיב הרישום עם קידומת ברירת המחדל "[LOG]" ומחזירה את המופע היחידני.

    הוסיפו את המתודות הבאות ל-Logger:

    • SetPrefix(prefix string) - משנה את הקידומת של רכיב הרישום
    • Log(message string) string - מגדילה את מספר ההודעות ומחזירה מחרוזת בפורמט: [prefix] #[count]: [message]
    • GetCount() int - מחזירה את המספר הכולל של ההודעות שנרשמו
  • main.go: הדגימו שקריאות מרובות ל-GetLogger() מחזירות את אותו המופע.

    קראו ערך חדש של קידומת, ואז קראו את מספר ההודעות ולאחריו את כל אחת מההודעות לרישום.

    קבלו את מופע רכיב הרישום, הגדירו את הקידומת המותאמת אישית, ואז רשמו כל הודעה והדפיסו את התוצאה. לאחר רישום כל ההודעות, קבלו שוב את מופע רכיב הרישום (כדי להוכיח שזה אותו מופע) והדפיסו את המספר הכולל של ההודעות.

הקלטים הבאים יסופקו:

  • שורה 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: קבלו את מופע ה-logger באמצעות GetLogger()

	// TODO: הגדירו את הקידומת המותאמת אישית באמצעות SetPrefix()

	// TODO: רשמו כל הודעה ביומן והדפיסו את התוצאה

	// TODO: קבלו שוב את מופע ה-logger (כדי להוכיח שזה אותו מופע)
	// והדפיסו את מספר ההודעות הכולל בפורמט: "Total messages logged: X"
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין