تغليف الأخطاء (fmt.Errorf)
جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 54 من 107.
عندما تمر الأخطاء عبر طبقات متعددة من تطبيقك، يصبح من الضروري معرفة مكان نشوء الخطأ. تتيح لك الدالة fmt.Errorf في Go، باستخدام معامل %w، تغليف الأخطاء مع الحفاظ على الخطأ الأصلي وإضافة سياق.
يُنشئ تغليف الأخطاء سلسلةً من الأخطاء. ويمكن لكل طبقة إضافة معلومات حول ما كانت تحاول فعله عند حدوث الخطأ:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// معالجة البيانات...
return nil
}الفعل %w مميّز. فهو يغلّف الخطأ الأصلي داخل الخطأ الجديد. وهذا يختلف عن %v، الذي يحوّل الخطأ فقط إلى سلسلة نصية ويفقد هوية الخطأ الأصلي.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}عندما تفشل LoadSettings، تعرض رسالة الخطأ السلسلة الكاملة: "loading settings: reading config file: open settings.json: no such file or directory". تضيف كل طبقة سياقًا، مما يجعل تصحيح الأخطاء أسهل بكثير.
لا يزال الخطأ المُغلَّف يحتوي على الخطأ الأصلي بداخله. في الدرس التالي، ستتعلّم كيفية فكّ تغليف هذه الأخطاء والتحقق مما بداخلها باستخدام errors.Is() وerrors.As().
التحدي
سهللنَبْنِ نظامًا لمعالجة الطلبات يوضّح كيفية تغليف الأخطاء عبر طبقات متعددة. ستنشئ سلسلة من الدوال، حيث تضيف كل طبقة سياقًا إلى الأخطاء، مما يجعل تتبّع المكان الذي تحدث فيه المشكلات بالضبط أمرًا سهلًا.
ستنظّم التعليمات البرمجية في ملفين:
orders.go: أنشئ منطق معالجة الطلبات مع طبقات متعددة تغلّف الأخطاء أثناء انتشارها إلى الأعلى.نفّذ ثلاث دوال تشكّل سلسلة معالجة:
ValidateItem(itemID string) error- تُرجع خطأً برسالة"item not found"إذا كانت قيمة itemID هي"INVALID"، وإلا فتُرجعnilProcessOrder(orderID, itemID string) error- تستدعيValidateItem. إذا أعادت خطأً، فغلّفه بالسياق:"processing order [orderID]: %w". وإلا فأرجعnilSubmitOrder(customerName, orderID, itemID string) error- تستدعيProcessOrder. إذا أعادت خطأً، فغلّفه بالسياق:"submitting order for [customerName]: %w". وإلا فأرجعnil
يجب أن تستخدم كل طبقة
fmt.Errorfمع العامل%wلتغليف الخطأ القادم من الطبقة الأدنى، وبناء سلسلة من السياقات.main.go: اقرأ تفاصيل الطلب من الإدخال، واستدعِSubmitOrder، واعرض النتيجة. إذا حدث خطأ، فاطبع سلسلة الأخطاء الكاملة. وإذا نجحت العملية، فاطبع رسالة تأكيد.
ستُوفَّر المدخلات التالية:
- السطر 1: اسم العميل
- السطر 2: معرّف الطلب
- السطر 3: معرّف العنصر
اطبع النتيجة:
- إذا حدث خطأ:
Error: [full error chain] - إذا نجحت العملية:
Order [orderID] submitted successfully for [customerName]
على سبيل المثال، عند إعطاء Alice وORD-123 وINVALID، يجب أن يكون الناتج:
Error: submitting order for Alice: processing order ORD-123: item not foundلاحظ كيف تعرض رسالة الخطأ السلسلة الكاملة. يمكنك تتبّع المشكلة من محاولة الإرسال على المستوى الأعلى، مرورًا بمعالجة الطلب، وصولًا إلى فشل التحقق الفعلي. أضافت كل طبقة سياقها الخاص باستخدام %w.
وعند إعطاء Bob وORD-456 وITEM-001، يجب أن يكون الناتج:
Order ORD-456 submitted successfully for Bobجرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// اقرأ اسم العميل
scanner.Scan()
customerName := scanner.Text()
// اقرأ معرف الطلب
scanner.Scan()
orderID := scanner.Text()
// اقرأ معرف العنصر
scanner.Scan()
itemID := scanner.Text()
// TODO: استدعِ SubmitOrder بالقيم المدخلة
// TODO: إذا حدث خطأ، اطبع: Error: [full error chain]
// TODO: If successful, print: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Poolتدرّب بنفسك: مترجم Go عبر الإنترنت