Menu
Coddy logo textTech

הסתרת מידע ב-Go

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

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

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

type Counter struct {
    value    int
    maxLimit int
}

func NewCounter(limit int) *Counter {
    return &Counter{maxLimit: limit}
}

func (c *Counter) Increment() bool {
    if c.value >= c.maxLimit {
        return false
    }
    c.value++
    return true
}

func (c *Counter) Value() int {
    return c.value
}

קוד חיצוני לא יודע ש־Counter משתמש ב־int באופן פנימי. הוא יכול להיות float64, פרוסה או משהו אחר לגמרי. אפשר לשנות את המימוש בלי להשפיע על שום קוד שמשתמש ב־Counter, כל עוד המתודות מתנהגות באותה צורה.

העיקרון הזה חל גם על פונקציות עזר. השאירו את הלוגיקה הפנימית לא מיוצאת:

// מיוצא - חלק מה-API הציבורי
func (c *Counter) Reset() {
    c.value = 0
}

// לא מיוצא - פונקציית עזר פנימית
func (c *Counter) isAtLimit() bool {
    return c.value >= c.maxLimit
}

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

challenge icon

אתגר

קל

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

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

  • wallet.go: צרו מבנה Wallet שמסתיר את כל המצב הפנימי שלו. הארנק צריך לעקוב באופן פנימי אחר היתרה הנוכחית ומגבלת ההוצאה, אך קוד חיצוני לא אמור לדעת כיצד הם מאוחסנים או מיוצגים. חשפו התנהגות באמצעות המתודות האלה:
    • NewWallet(limit float64) *Wallet - בנאי שיוצר ארנק עם מגבלת ההוצאה הנתונה ויתרה של אפס
    • Deposit(amount float64) bool - מוסיפה כסף לארנק, ומחזירה true אם הפעולה הצליחה (הסכום חייב להיות חיובי)
    • Spend(amount float64) bool - מוציאה כסף אם יש יתרה מספקת וגם הסכום אינו חורג ממגבלת ההוצאה, ומחזירה true אם הפעולה הצליחה
    • Balance() float64 - מחזירה את היתרה הנוכחית
    • Status() string - מחזירה הודעת מצב בתבנית: Balance: $[balance] (Limit: $[limit])
    השאירו כל לוגיקת עזר (כמו בדיקה אם עסקה תקינה) כפונקציות או מתודות לא מיוצאות. אלה פרטי מימוש פנימיים שעשויים להשתנות בהמשך.
  • main.go: קראו את הגדרות הארנק והעסקאות מהקלט, צרו ארנק, בצעו פעולות והציגו את התוצאות לאחר כל פעולה.

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

  • שורה 1: מגבלת הוצאה
  • שורה 2: סכום ההפקדה
  • שורה 3: סכום ההוצאה הראשונה
  • שורה 4: סכום ההוצאה השנייה

לאחר יצירת הארנק, הדפיסו את מצבו ההתחלתי. לאחר מכן בצעו כל פעולה והדפיסו את המצב המעודכן (אם הפעולה הצליחה) או את Transaction failed (אם הפעולה נדחתה). עצבו את כל הסכומים בדולרים עם שתי ספרות אחרי הנקודה העשרונית.

לדוגמה, עבור 50, 100, 30 ו-80, הפלט שלכם אמור להיות:

Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failed

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

נסו בעצמכם

package main

import (
	"fmt"
)

func main() {
	// קרא את הקלטים
	var limit float64
	var depositAmount float64
	var spend1 float64
	var spend2 float64

	fmt.Scanln(&limit)
	fmt.Scanln(&depositAmount)
	fmt.Scanln(&spend1)
	fmt.Scanln(&spend2)

	// TODO: צור ארנק חדש עם מגבלת ההוצאה הנתונה

	// TODO: הדפס את המצב ההתחלתי

	// TODO: בצע הפקדה והדפס את התוצאה (מצב או "Transaction failed")

	// TODO: בצע את ההוצאה הראשונה והדפס את התוצאה

	// TODO: בצע את ההוצאה השנייה והדפס את התוצאה
}
quiz iconבחנו את עצמכם

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

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

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