Menu
Coddy logo textTech

מערכת בנקאית

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

challenge icon

אתגר

קל

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

תארגן את הקוד שלך בחמישה קבצים:

  • account.go: הגדר ממשק Transactable עם המתודות Deposit(amount float64) error ו-Withdraw(amount float64) error. לאחר מכן, צור שני סוגי חשבונות שמממשים את הממשק הזה:

    CheckingAccount עם השדות הלא מיוצאים id, holder ו-balance. חשבונות עו״ש מאפשרים משיכות שמביאות את היתרה בדיוק לאפס, אך לא מתחתיו.

    SavingsAccount עם אותם שדות לא מיוצאים, וכן שדה minBalance. חשבונות חיסכון חייבים לשמור על יתרת מינימום של 100.0: משיכות שיורידו את היתרה מתחת לסף הזה צריכות להיכשל.

    שני סוגי החשבונות זקוקים לבנאים (NewCheckingAccount ו-NewSavingsAccount) ולמתודת getter בשם Balance() float64. בהפקדות, דחה סכומים שליליים או אפס. במשיכות, דחה סכומים שליליים או אפס וכן סכומים שעולים על הכספים הזמינים.

  • transaction.go: צור מבנה Transaction לתיעוד הפעילות בחשבון. לכל עסקה יש Type (מחרוזת: "deposit" או "withdrawal"), Amount (float64) ו-AccountID (מחרוזת). הוסף בנאי בשם NewTransaction ומתודה בשם String() string שמחזירה את הפורמט: [Type] $[Amount] on [AccountID], כאשר הסכום מעוצב עם שתי ספרות אחרי הנקודה העשרונית.
  • bank.go: בנה מבנה Bank שמנהל כמה חשבונות ומתעד עסקאות. אחסן את החשבונות במפה לפי מזהה ושמור פרוסה של עסקאות. ממש את הפעולות הבאות:
    • NewBank() *Bank: מאתחלת אוספים ריקים
    • AddAccount(id string, account Transactable): רושמת חשבון
    • Deposit(accountID string, amount float64) error: מפקידה לחשבון ומתעדת את העסקה במקרה של הצלחה
    • Withdraw(accountID string, amount float64) error: מושכת מחשבון ומתעדת את העסקה במקרה של הצלחה
    • Transfer(fromID, toID string, amount float64) error: מעבירה כסף בין חשבונות באופן אטומי (אם המשיכה מצליחה אך ההפקדה נכשלת, יש לבטל את המשיכה)
    • GetBalance(accountID string) (float64, error): מחזירה את יתרת החשבון

    החזר שגיאות מתאימות עבור חשבונות שאינם קיימים.

  • errors.go: הגדר שגיאות sentinel עבור הפעולות הבנקאיות שלך:
    • ErrAccountNotFound: כאשר מזהה חשבון אינו קיים
    • ErrInsufficientFunds: כאשר סכום המשיכה עולה על היתרה הזמינה
    • ErrInvalidAmount: כאשר סכום ההפקדה או המשיכה הוא אפס או שלילי
    • ErrMinBalanceRequired: כאשר משיכה מחשבון חיסכון תפר את דרישת יתרת המינימום
  • main.go: צור בנק ועבד פעולות על סמך הקלט.

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

    לאחר מכן, קרא את מספר הפעולות. כל פעולה היא אחת מהבאות:

    • deposit [accountID] [amount]
    • withdraw [accountID] [amount]
    • transfer [fromID] [toID] [amount]
    • balance [accountID]

    עבור כל פעולה, הדפס את התוצאה: OK עבור הפקדות, משיכות או העברות מוצלחות; את היתרה בפורמט של שתי ספרות אחרי הנקודה העשרונית עבור שאילתות יתרה; או Error: [message] במקרה של כשל, תוך שימוש בהודעות של שגיאות ה-sentinel שלך.

הקלט שיסופק יהיה במבנה הבא:

  • מספר החשבונות, ולאחר מכן סוג החשבון, המזהה, בעל החשבון והיתרה הראשונית של כל חשבון (4 שורות לכל חשבון)
  • מספר הפעולות, ולאחר מכן כל פעולה (שורה אחת לכל פעולה)

לדוגמה, עבור הקלט:

2
checking
ACC001
Alice
500.00
savings
ACC002
Bob
1000.00
6
deposit ACC001 200
balance ACC001
withdraw ACC002 950
withdraw ACC001 800
transfer ACC001 ACC002 100
balance ACC002

הפלט שלך צריך להיות:

OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00

ועבור הקלט:

1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50

הפלט שלך צריך להיות:

OK
0.00
Error: invalid amount
Error: account not found

שים לב כיצד הממשק Transactable מאפשר ל-Bank לעבוד באופן אחיד עם שני סוגי החשבונות, בעוד שכל סוג חשבון אוכף את הכללים שלו. המתודה Transfer מדגימה פעולות אטומיות: אם משהו משתבש באמצע, המערכת נשארת עקבית!

נסו בעצמכם

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// קרא את מספר החשבונות
	scanner.Scan()
	numAccounts, _ := strconv.Atoi(scanner.Text())

	// צור בנק חדש
	bank := NewBank()

	// קרא וצור חשבונות
	for i := 0; i < numAccounts; i++ {
		scanner.Scan()
		accountType := scanner.Text()
		scanner.Scan()
		id := scanner.Text()
		scanner.Scan()
		holder := scanner.Text()
		scanner.Scan()
		balance, _ := strconv.ParseFloat(scanner.Text(), 64)

		// TODO: צור את סוג החשבון המתאים לפי accountType
		// והוסף אותו לבנק
		_ = accountType
		_ = id
		_ = holder
		_ = balance
	}

	// קרא את מספר הפעולות
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// עבד כל פעולה
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		parts := strings.Fields(scanner.Text())
		operation := parts[0]

		// TODO: טפל בכל סוג פעולה:
		// - "deposit": הפקד לחשבון, הדפס "OK" או שגיאה
		// - "withdraw": משוך מהחשבון, הדפס "OK" או שגיאה
		// - "transfer": העבר בין חשבונות, הדפס "OK" או שגיאה
		// - "balance": קבל והדפס את היתרה בפורמט של 2 ספרות אחרי הנקודה העשרונית
		_ = operation
	}
}

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

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