واجهة sort.Interface
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 74 من 107.
توفر حزمة sort مثالاً ممتازاً آخر للتصميم القائم على الواجهات (interface-based design). لفرز مجموعة مخصصة، يجب أن يطبق نوعك الواجهة sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}توفر هذه الطرق الثلاث لخوارزمية الفرز كل ما تحتاجه: طول المجموعة، وطريقة لمقارنة العناصر، وطريقة لتبديلها. إليك كيفية جعل شريحة (slice) من الهياكل المخصصة (custom structs) قابلة للفرز:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}الفكرة الأساسية هي إنشاء نوع مسمى (ByAge) بناءً على الشريحة (slice) الخاصة بك. يتيح لك هذا تحديد سلوكيات فرز مختلفة لنفس البيانات. يمكنك إنشاء ByName بتنفيذ مختلف لـ Less للفرز أبجديًا بدلاً من ذلك.
بمجرد أن يستوفي النوع الخاص بك sort.Interface، فإنه يعمل مع sort.Sort() و sort.Reverse() و sort.IsSorted() تلقائياً.
التحدي
سهللنقم ببناء نظام مخزون منتجات قابل للفرز يوضح قوة sort.Interface في لغة Go! ستقوم بإنشاء نوع Product وتنفيذ استراتيجيات فرز متعددة، مما يسمح بفرز نفس المجموعة بطرق مختلفة.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
product.go: حدد نوع المنتج الخاص بك وتنفيذات الفرز.قم بإنشاء هيكل
Productبثلاثة حقول:Name(string)، وPrice(float64)، وQuantity(int).قم بإنشاء نوعين مسميين بناءً على
[]Product:ByPrice- لفرز المنتجات حسب السعر بترتيب تصاعديByQuantity- لفرز المنتجات حسب الكمية بترتيب تنازلي (الكمية الأعلى أولاً)
يحتاج كل نوع إلى تنفيذ الطرق الثلاث المطلوبة بواسطة
sort.Interface: وهيLen()، وLess(i, j int)، وSwap(i, j int). تحدد طريقةLessترتيب الفرز لكل نوع.main.go: بناء وفرز مخزون المنتجات الخاص بك.اقرأ وضع الفرز (
priceأوquantity)، ثم اقرأ عدداً متبوعاً بتفاصيل المنتج. يتم توفير كل منتج في ثلاثة أسطر: الاسم، والسعر، والكمية.قم بإنشاء شريحة (slice) من المنتجات، وقم بفرزها باستخدام نوع الفرز المناسب بناءً على الوضع، ثم اطبع كل منتج بهذا التنسيق:
[Name]: $[Price] (x[Quantity])اعرض الأسعار بمنزلتين عشريتين.
سيتم توفير المدخلات التالية:
- السطر 1: وضع الفرز (
priceأوquantity) - السطر 2: عدد المنتجات
- الأسطر التالية: تفاصيل المنتج (الاسم، السعر، الكمية - ثلاثة أسطر لكل منتج)
على سبيل المثال، إذا كان المعطى:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25يجب أن يكون مخرجك:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)وإذا كان المعطى:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25يجب أن يكون مخرجك:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)لاحظ كيف يمكن فرز نفس بيانات المنتج بشكل مختلف ببساطة عن طريق استخدام نوع مسمى مختلف. بمجرد أن تستوفي أنواعك sort.Interface، فإنها تعمل بسلاسة مع sort.Sort() من المكتبة القياسية.
ورقة مرجعية
تتطلب حزمة sort من الأنواع تنفيذ sort.Interface لتكون قابلة للفرز:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}لجعل شريحة (slice) مخصصة قابلة للفرز، قم بإنشاء نوع مسمى وقم بتنفيذ الطرق الثلاث المطلوبة:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// فرز المجموعة
sort.Sort(ByAge(people))يمكنك إنشاء أنواع مسمّاة متعددة لنفس الشريحة لتنفيذ سلوكيات فرز مختلفة. يحدد كل نوع طريقة Less الخاصة به لتحديد ترتيب الفرز.
بمجرد أن يستوفي النوع sort.Interface، فإنه يعمل مع sort.Sort() و sort.Reverse() و sort.IsSorted().
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// قراءة وضع الترتيب
var mode string
fmt.Fscanln(reader, &mode)
// قراءة عدد المنتجات
var count int
fmt.Fscanln(reader, &count)
// قراءة المنتجات
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = name[:len(name)-1] // إزالة سطر جديد
priceStr, _ := reader.ReadString('\n')
priceStr = priceStr[:len(priceStr)-1]
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = qtyStr[:len(qtyStr)-1]
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: فرز المنتجات بناءً على الوضع (mode)
// إذا كان الوضع "price"، استخدم نوع ByPrice
// إذا كان الوضع "quantity"، استخدم نوع ByQuantity
// استخدم sort.Sort() مع النوع المناسب
// TODO: اطبع كل منتج بالتنسيق التالي:
// [Name]: $[Price] (x[Quantity])
// استخدم fmt.Printf مع %.2f لتنسيق السعر
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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