حل بديل للـ Methods العامة
جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 71 من 107.
لدى Go قيد ملحوظ: لا يمكنك تعريف methods مع own type parameters على struct. وعلى الرغم من أن generic structs تعمل بشكل رائع، فإن إضافة method تُعرّف new type parameter ليست allowed.
لن يتم تجميع هذا الرمز:
type Box[T any] struct {
Value T
}
// ERROR: يجب ألا تحتوي الطريقة على معاملات نوع
func (b Box[T]) Convert[U any](fn func(T) U) U {
return fn(b.Value)
}الحل البديل هو استخدام دالة generic مستقلة بدلًا من method:
type Box[T any] struct {
Value T
}
// دالة عادية - تستخدم معامل النوع الخاص بالـ struct
func (b Box[T]) Get() T {
return b.Value
}
// دالة مستقلة - يمكن أن يكون لها معاملات نوع خاصة بها
func Convert[T, U any](b Box[T], fn func(T) U) U {
return fn(b.Value)
}
func main() {
intBox := Box[int]{Value: 42}
// استخدم الدالة
fmt.Println(intBox.Get()) // 42
// استخدم الدالة المستقلة
strResult := Convert(intBox, func(n int) string {
return fmt.Sprintf("Number: %d", n)
})
fmt.Println(strResult) // Number: 42
}لا يزال بإمكان الأساليب الموجودة في البُنى العامة استخدام مُعامل النوع T الخاص بالبنية. ينطبق القيد فقط على تقديم مُعاملات نوع إضافية في توقيع الأسلوب.
عندما تحتاج إلى تلك المرونة، فإن generic function التي تأخذ البنية باعتبارها وسيطها الأول تحقق النتيجة نفسها.
التحدي
سهللنبنِ toolkit لتحويل Data يوضّح الحل البديل لقيد Go على generic methods! بما أن methods لا يمكنها تقديم type parameters جديدة، ستنشئ generic functions مستقلة تحقق المرونة نفسها.
ستنظّم code عبر ملفين:
wrapper.go: Define حاوية generic و functions للتحويل.أنشئ generic struct باسم
Wrapper[T any]مع field واحد هوValueمن النوعT.Add method باسم
GetعلىWrapper[T]يعيد القيمة المغلّفة. يستخدم هذا method type parameter الخاص بالـ struct، وهو أمر allowed.أنشئ generic function مستقلة باسم
Transform[T, U any](w Wrapper[T], fn func(T) U) Uتطبّق function التحويل على قيمة الحاوية وتعيد results. تحتاج هذه function إلى type parameter خاص بها، وهوUلنوع الخرج، ولهذا يجب أن تكون function مستقلة وليست method.أنشئ function مستقلة أخرى باسم
TransformToString[T any](w Wrapper[T]) stringتحوّل القيمة المغلّفة إلى string باستخدامfmt.Sprintf("%v", ...).main.go: اعرض نمط الحل البديل باستخدام تحويلات مختلفة.اقرأ indicator للنوع (
intأوstring)، ثم اقرأ قيمة. أنشئWrapperمن النوع المناسب.بالنسبة إلى إدخال
int: أنشئWrapper[int]، ثم استخدمTransformلمضاعفة القيمة (مع إرجاع int)، واستخدمTransformToStringللحصول على representation نصية.بالنسبة إلى إدخال
string: أنشئWrapper[string]، ثم استخدمTransformللحصول على length الخاص بـ string (مع إرجاع int)، واستخدمTransformToStringللحصول على representation النصية.Print النتائج بهذا format:
Original: [value] Transformed: [transformed value] As String: [string representation]
سيتم توفير inputs التالية:
- السطر 1: indicator النوع (
intأوstring) - السطر 2: القيمة
على سبيل المثال، إذا كانت المدخلات:
int
25فيجب أن يكون output لديك:
Original: 25
Transformed: 50
As String: 25وإذا كانت المدخلات:
string
Hello Worldفيجب أن يكون output لديك:
Original: Hello World
Transformed: 11
As String: Hello Worldالفكرة الأساسية هي أن Transform تأخذ Wrapper[T] باعتباره argument الأول وتقدّم type parameter جديدًا هو U لنوع الإرجاع. وهذا أمر لم يكن ممكنًا بوصفه method على الـ struct. يمنحك هذا النمط مرونة generic transformations مع العمل ضمن قيود type system في Go.
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// اقرأ مؤشر النوع
typeIndicator, _ := reader.ReadString('\n')
typeIndicator = strings.TrimSpace(typeIndicator)
// اقرأ القيمة
value, _ := reader.ReadString('\n')
value = strings.TrimSpace(value)
if typeIndicator == "int" {
// حلل قيمة العدد الصحيح
num, _ := strconv.Atoi(value)
// TODO: أنشئ Wrapper[int] بالعدد المحلل
// TODO: استخدم Transform لمضاعفة القيمة (تلميح: مرر دالة تضاعف)
// TODO: استخدم TransformToString للحصول على التمثيل النصي
// TODO: اطبع النتائج بالتنسيق المطلوب:
// Original: [value]
// Transformed: [transformed value]
// As String: [string representation]
_ = num // احذف هذا السطر عندما تستخدم num
} else if typeIndicator == "string" {
// TODO: أنشئ Wrapper[string] بالقيمة
// TODO: استخدم Transform للحصول على طول السلسلة (تلميح: مرّر دالة تُرجع len())
// TODO: استخدم TransformToString للحصول على التمثيل النصي
// TODO: اطبع النتائج بالتنسيق المطلوب:
// Original: [value]
// Transformed: [transformed value]
// As String: [string representation]
_ = value // احذف هذا السطر عندما تستخدم value
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت