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

واجهة error

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 52 من 107.

في لغة Go، يتم التعامل مع الأخطاء من خلال واجهة برمجية (interface) مدمجة تسمى error. هذه الواجهة بسيطة للغاية—فهي تتطلب طريقة (method) واحدة فقط:

type error interface {
    Error() string
}

أي نوع يقوم بتنفيذ طريقة Error() تعيد سلسلة نصية يحقق تلقائياً واجهة error. هذا هو نظام الواجهات في لغة Go قيد العمل - لا حاجة لإعلان صريح.

الطريقة الأكثر شيوعاً لإنشاء الأخطاء هي استخدام حزمة errors:

import "errors"

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}

عادةً ما تُرجع دوال Go خطأً كقيمة إرجاع أخيرة لها. عندما لا يحدث خطأ، فإنك تُرجع nil. ثم يتحقق المستدعي مما إذا كان الخطأ غير فارغ (non-nil) قبل المتابعة:

result, err := Divide(10, 0)
if err != nil {
    fmt.Println("Error:", err.Error())
    return
}
fmt.Println("Result:", result)

يمكنك أيضاً استخدام fmt.Errorf لإنشاء رسائل خطأ منسقة:

func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", fmt.Errorf("invalid user id: %d", id)
    }
    return "Alice", nil
}

هذا النمط من إرجاع الأخطاء بدلاً من طرح الاستثناءات هو أمر أساسي في لغة Go. فهو يجعل معالجة الأخطاء صريحة ويجبرك على التفكير فيما يحدث عندما تسوء الأمور - وهو جانب رئيسي في كتابة كود قوي وموجه للكائنات.

challenge icon

التحدي

سهل

لنقم ببناء نظام حساب بنكي يوضح نمط معالجة الأخطاء في لغة Go. ستقوم بإنشاء حساب بسيط يتحقق من صحة العمليات ويعيد أخطاءً ذات معنى عند حدوث خطأ ما.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • account.go: قم بإنشاء هيكل BankAccount يحتوي على حقل Owner مُصدر (string) وحقل balance غير مُصدر (float64). قم بتنفيذ ما يلي:
    • NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error) - منشئ (constructor) يعيد خطأً بالرسالة "initial deposit must be positive" إذا كان الإيداع الأولي صفراً أو سالباً. بخلاف ذلك، قم بإنشاء الحساب وإعادته.
    • Deposit(amount float64) error - يضيف المال إلى الرصيد. أعد خطأً بالرسالة "deposit amount must be positive" إذا كان المبلغ صفراً أو سالباً.
    • Withdraw(amount float64) error - يسحب المال من الرصيد. أعد خطأً بالرسالة "withdrawal amount must be positive" إذا كان المبلغ صفراً أو سالباً، أو "insufficient funds" إذا كان الرصيد أقل من مبلغ السحب.
    • Balance() float64 - يعيد الرصيد الحالي.
  • main.go: اقرأ تفاصيل الحساب ومبالغ العمليات من المدخلات. حاول إنشاء حساب وإجراء العمليات. بعد كل عملية، اطبع إما رسالة النجاح أو رسالة الخطأ.

سيتم توفير المدخلات التالية:

  • السطر 1: اسم المالك (Owner name)
  • السطر 2: مبلغ الإيداع الأولي
  • السطر 3: مبلغ الإيداع
  • السطر 4: مبلغ السحب

لكل عملية، اطبع النتيجة:

  • بعد إنشاء الحساب: Account created for [Owner] with balance: $[balance] أو Error: [error message]
  • بعد الإيداع: Deposited successfully. New balance: $[balance] أو Error: [error message]
  • بعد السحب: Withdrew successfully. New balance: $[balance] أو Error: [error message]

قم بتنسيق جميع قيم الرصيد بمنزلتين عشريتين. إذا فشل إنشاء الحساب، فتخطى العمليات المتبقية.

على سبيل المثال، إذا كانت المدخلات Alice، و 100، و 50، و 200، يجب أن يكون مخرجك:

Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient funds

وإذا كانت المدخلات Bob، و -50، و 25، و 10، يجب أن يكون مخرجك:

Error: initial deposit must be positive

ورقة مرجعية

تتعامل لغة Go مع الأخطاء من خلال واجهة error المدمجة، والتي تتطلب طريقة (method) واحدة فقط:

type error interface {
    Error() string
}

قم بإنشاء الأخطاء باستخدام حزمة errors:

import "errors"

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}

تعيد الدوال الأخطاء كقيمة إرجاع أخيرة. قم بإرجاع nil عند عدم حدوث خطأ:

result, err := Divide(10, 0)
if err != nil {
    fmt.Println("Error:", err.Error())
    return
}
fmt.Println("Result:", result)

استخدم fmt.Errorf لرسائل الخطأ المنسقة:

func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", fmt.Errorf("invalid user id: %d", id)
    }
    return "Alice", nil
}

جرّب بنفسك

package main

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

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

	// قراءة اسم المالك
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)

	// قراءة مبلغ الإيداع الأولي
	initialDepositStr, _ := reader.ReadString('\n')
	initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)

	// قراءة مبلغ الإيداع
	depositStr, _ := reader.ReadString('\n')
	depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)

	// قراءة مبلغ السحب
	withdrawStr, _ := reader.ReadString('\n')
	withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)

	// TODO: إنشاء حساب بنكي جديد باستخدام NewBankAccount
	// إذا حدث خطأ، اطبع "Error: [error message]" وقم بالإنهاء (return)
	// إذا نجحت العملية، اطبع "Account created for [Owner] with balance: $[balance]"

	// TODO: محاولة إيداع مبلغ الإيداع
	// إذا حدث خطأ، اطبع "Error: [error message]"
	// إذا نجحت العملية، اطبع "Deposited successfully. New balance: $[balance]"

	// TODO: محاولة سحب مبلغ السحب
	// إذا حدث خطأ، اطبع "Error: [error message]"
	// إذا نجحت العملية، اطبع "Withdrew successfully. New balance: $[balance]"

	// تذكر تنسيق قيم الرصيد بمنزلتين عشريتين باستخدام fmt.Printf("%.2f", balance)

	_ = ownerName
	_ = initialDeposit
	_ = depositAmount
	_ = withdrawAmount
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming