واجهات Stringer و Error
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 32 من 107.
تحدد مكتبة Go القياسية واجهتين ستستخدمهما باستمرار: fmt.Stringer و error. كلاهما واجهتان بسيطتان أحاديتا الطريقة تمنحان أنواعك تكاملاً قوياً مع وظائف Go المدمجة.
توجد واجهة Stringer في حزمة fmt وتتطلب طريقة واحدة فقط:
type Stringer interface {
String() string
}عندما تقوم بتنفيذ String() على النوع الخاص بك، فإن دوال مثل fmt.Println تستخدمه تلقائيًا لعرض قيمتك:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30 عاماً)
}تُعد واجهة error بسيطة بنفس القدر، حيث تتطلب دالة Error() تُعيد سلسلة نصية:
type error interface {
Error() string
}أي نوع يحتوي على دالة Error() يمكن استخدامه كقيمة خطأ في Go. يتيح لك ذلك إنشاء أنواع أخطاء مخصصة مع سياق إضافي:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: تنسيق غير صالح
}توضح هاتان الواجهتان فلسفة لغة Go: واجهات صغيرة ومركزة تتيح سلوكاً قوياً من خلال التنفيذ الضمني.
التحدي
سهللنقم ببناء نظام لمراقبة درجة الحرارة يستخدم واجهتي Stringer و error لتوفير مخرجات قابلة للقراءة ومعالجة هادفة للأخطاء.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
temperature.go: قم بإنشاء هيكلTemperatureيحتوي على حقولValue(float64) وUnit(string). قم بتنفيذ طريقةString()بحيث تعرض درجة الحرارة بتنسيق سهل القراءة عند طباعتها. قم أيضاً بإنشاء هيكلTemperatureErrorيحتوي على حقولTemp(float64) وReason(string)، وقم بتنفيذ طريقةError()لتقديم رسائل خطأ وصفية.main.go: قم بإنشاء دالة تسمىValidateTemperatureتأخذTemperatureوتعيد خطأً إذا كانت درجة الحرارة غير صالحة. تكون درجة الحرارة غير صالحة إذا كانت أقل من الصفر المطلق (-273.15 للدرجة المئوية، -459.67 للفهرنهايت). إذا كانت صالحة، تعيد الدالةnil. اقرأ بيانات درجة الحرارة من المدخلات، وأنشئ Temperature، وقم بالتحقق من صحتها، واطبع المخرجات المناسبة.
سيتم توفير المدخلات التالية:
- السطر 1: قيمة درجة الحرارة (float)
- السطر 2: الوحدة (
Cللدرجة المئوية أوFللفهرنهايت)
يجب أن تعيد طريقة String() الخاصة بك التنسيق التالي:
[Value] degrees [Unit]حيث Unit هي الكلمة الكاملة "Celsius" أو "Fahrenheit" بناءً على المدخلات.
يجب أن تعيد طريقة Error() الخاصة بك التنسيق التالي:
invalid temperature [Temp]: [Reason]إذا كانت درجة الحرارة صالحة، اطبع Temperature باستخدام fmt.Println (والتي ستستخدم طريقة String() الخاصة بك). إذا كانت غير صالحة، اطبع الخطأ (والذي سيستخدم طريقة Error() الخاصة بك).
على سبيل المثال، إذا كانت المدخلات 25.5 و C، يجب أن تكون مخرجاتك:
25.5 degrees Celsiusوإذا كانت المدخلات -500 و F، يجب أن تكون مخرجاتك:
invalid temperature -500: below absolute zeroاستخدم "below absolute zero" كسبب (reason) عندما تكون درجة الحرارة منخفضة جداً.
ورقة مرجعية
تُعرف مكتبة Go القياسية واجهتين أساسيتين: fmt.Stringer و error.
واجهة Stringer
تتطلب واجهة Stringer من حزمة fmt طريقة واحدة:
type Stringer interface {
String() string
}عندما تقوم بتنفيذ String() على النوع الخاص بك، فإن دوال مثل fmt.Println تستخدمها تلقائيًا:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30 years old)
}واجهة error
تتطلب واجهة error طريقة Error() التي تُرجع سلسلة نصية (string):
type error interface {
Error() string
}يمكن استخدام أي نوع يحتوي على طريقة Error() كقيمة خطأ، مما يسمح بأنواع أخطاء مخصصة مع سياق إضافي:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: invalid format
}جرّب بنفسك
package main
import (
"fmt"
)
// ValidateTemperature يتحقق مما إذا كانت درجة الحرارة صالحة
// يعيد خطأ إذا كانت درجة الحرارة أقل من الصفر المطلق
// الصفر المطلق: -273.15 للدرجة المئوية، -459.67 للفهرنهايت
func ValidateTemperature(t Temperature) error {
// TODO: تنفيذ منطق التحقق
// التحقق مما إذا كانت درجة الحرارة أقل من الصفر المطلق بناءً على الوحدة
// إرجاع TemperatureError إذا كانت غير صالحة، و nil إذا كانت صالحة
return nil
}
func main() {
var value float64
var unit string
fmt.Scanln(&value)
fmt.Scanln(&unit)
// TODO: إنشاء هيكل Temperature بالقيم المدخلة
// TODO: التحقق من صحة درجة الحرارة باستخدام ValidateTemperature
// TODO: إذا كان هناك خطأ، قم بطباعة الخطأ
// خلاف ذلك، قم بطباعة درجة الحرارة (والتي ستستخدم طريقة String())
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
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