Go / الأساسي: القيم وعقود البرامج / الدرس 6
الطرق والواجهات والأخطاء
استخدم عقود سلوك صغيرة وأخطاء صريحة مع فصل النوع الديناميكي والقيمة الديناميكية وnil.
في هذه الصفحة
أهداف الدرس
- شرح مجموعات الطرق وتحقيق الواجهة ضمنيا.
- تمييز واجهة nil من واجهة تحمل nil ذا نوع.
- تغليف الأخطاء وفحصها دون مطابقة الرسائل.
الواجهة تصف سلوكا
تربط الطريقة سلوكا بنوع المستقبل. تسرد واجهة صغيرة الطرق التي يحتاجها المستهلك، ويحققها النوع ضمنيا عند تطابق مجموعة طرقه. يتيح ذلك الاستبدال دون علاقة وراثة معلنة. ضع الواجهات قرب المستهلك عندما يوضح المطلوب بدلا من تصدير واجهة كبيرة تخمينية.
يعمل مستقبل القيمة على نسخة، لكن الحقول المرجعية قد تشارك التخزين. يستطيع مستقبل المؤشر تعديل الهدف ويؤثر في مجموعة الطرق: يملك *T طرق T و*T، ولا تضم مجموعة T تلقائيا طرق مستقبل *T. لا تغير تسهيلات صياغة الاستدعاء قواعد تحقيق الواجهة.
لـnil نوع وسياق
يمكن فهم قيمة الواجهة كنوع ديناميكي مقترن بقيمة ديناميكية. تساوي nil فقط عند غياب كليهما. يمنح إسناد *Label صفري إلى Namer الواجهة نوعا ديناميكيا فتكون غير nil. قد يصح استدعاء طريقة إذا عالجت المستقبل الصفري عمدا دون فك إشارته.
يعرض المثال التمييز بأمان. لا يتنبأ بهلع من كل طريقة على nil ذي نوع؛ يحدد الجسم هل يصل لكائن غائب. لا يثبت فحص الواجهة بـnil أن كل مستقبل ديناميكي غير صفري. تجنب القيم الاختيارية الملتبسة عندما تكون نتيجة صريحة أو مؤشر ملموس أوضح.
package main
import "fmt"
type Namer interface{ Name() string }
type Label struct{ Text string }
func (p *Label) Name() string {
if p == nil {
return "absent"
}
return p.Text
}
func main() {
var p *Label
var n Namer = p
fmt.Println(p == nil, n == nil, n.Name())
}
true false absent
المؤشر الملموس nil والواجهة ليست كذلك، وتعيد الطريقة الواعية بالصفري absent.
الأخطاء نتائج قابلة للفحص
تتطلب واجهة error المدمجة Error() string. أعد واجهة خطأ nil حقيقية عند النجاح؛ فقد ينتج إرجاع مؤشر خطأ nil ذي نوع واجهة غير nil وإشارة فشل زائفة. خصص panic للحالات الاستثنائية المناسبة لعقده، لا للمدخل غير الصالح المعتاد وفشل الإدخال المتوقع.
ينبغي فحص الخطأ قبل اعتبار النتيجة المصاحبة قابلة للاستخدام. تنقل الرسالة السياق للناس، ولا تحللها عادة كبروتوكول ثابت. استخدم علامات موثقة مع errors.Is أو أنواعا موثقة مع errors.As للقرارات الآلية.
حفظ علاقة خطأ مقصودة
تضيف fmt.Errorf باستخدام %w سياقا وتحفظ علاقة فك التغليف. يستطيع البرنامج التالي معرفة ErrMissing عبر الرسالة الخارجية. التغليف قرار واجهة: يكشف هوية الخطأ للمستدعي وقد يقيد تغيير التنفيذ لاحقا. استخدم %v عندما ينبغي عبور النص وحده.
لا تقارن errors.New("missing") جديدة بالعلامة متوقعا هوية من النص. الأخطاء المنشأة منفصلة لهذا الغرض. وثق الأخطاء المسموح فحصها وهل تظل النتيجة قابلة للاستخدام جزئيا عند الفشل.
package main
import (
"errors"
"fmt"
)
var ErrMissing = errors.New("missing")
func load() error { return fmt.Errorf("load: %w", ErrMissing) }
func main() {
err := load()
fmt.Println(errors.Is(err, ErrMissing), err)
}
true load: missing
تتبع errors.Is علاقة التغليف وتحدد العلامة الأصلية.
استخدام تأكيد النوع بخطة فشل
يستخرج تأكيد النوع قيمة ديناميكية وفق نوع مزعوم. تبلغ صيغة comma-ok عدم التطابق دون هلع؛ وتعالج type switch عدة بدائل موثقة. لا تجعل any عذرا لتأجيل كل قرار نوعي للتشغيل. يمنح عقد ملموس صغير أخطاء أوضح وحالات غير صالحة أقل.
قد تخفي الواجهات حالة مشتركة قابلة للتعديل خلف الطرق، فتحتاج قابلية الاستبدال وعودا دلالية عن التعديل والحجب والتزامن. تطابق اسم الطريقة الجزء البنيوي فقط. وثق هل يحجب التنفيذ أو يحتفظ بالمدخل أو يقبل استدعاء متزامنا.
الأخطاء الشائعة وإصلاحها
تُعامل عودة ناجحة كخطأ.
- لماذا يحدث؟
- حُول مؤشر nil ذو نوع إلى error.
- كيف تصلحه؟
- أعد واجهة nil عادية في مسار النجاح.
- كيف تتجنب تكراره؟
- اختبر النجاح والفشل عبر نوع الواجهة المعلن.
يسبب تأكيد نوع هلعا لبديل صالح.
- لماذا يحدث؟
- افترضت الشيفرة نوعا ديناميكيا دون فحص.
- كيف تصلحه؟
- استخدم comma-ok أو type switch بعقد موثق.
- كيف تتجنب تكراره؟
- اختبر كل نوع مدعوم ونوعا غير مدعوم.
ينكسر التعامل مع الخطأ بعد تعديل الرسالة.
- لماذا يحدث؟
- تقارن القرارات نصوصا بشرية.
- كيف تصلحه؟
- استخدم errors.Is أو errors.As بفئات موثقة.
- كيف تتجنب تكراره؟
- افصل الصياغة من هوية الخطأ الثابتة.
اختبر فهمك
1. تملك دالة نتيجة error ومتغير var problem *MyError دون كائن. ما عودة النجاح؟
اعرض الحل المشروح
أعد nil صراحة عند غياب الخطأ. يحول إرجاع problem مؤشرا صفريا ذا نوع *MyError إلى واجهة error فتكون غير nil إذا حقق النوع الواجهة. اسند النتيجة إلى error في اختبار النجاح واشترط err == nil. لا تصلح ذلك بتحليل تمثيل <nil> المطبوع.
2. لماذا قد يكون تغليف علامة خاصة بقاعدة بيانات التزام توافق عام؟
اعرض الحل المشروح
يستطيع مستخدم errors.Is ملاحظة العلامة عبر الغلاف والتفرع عليها. قد يغير استبدال القاعدة سلوكه ولو تطابقت الرسالة. قرر كشف الهوية أو ترجمتها لفئة يملكها المكون. وثق الفئة واختبر السلوك العام لا تفاصيل التخزين الخاصة.
أهم ما تعلمته
- تعتمد nil للواجهة على النوع والقيمة الديناميكيين.
- توافق مجموعة الطرق جزء من عقد السلوك فقط.
- يجب أن تكون هوية الخطأ وتغليفه قرارين عامين مقصودين.