تسلسل الدوال (Method Chaining)
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 81 من 107.
تسلسل الأساليب (Method chaining) هي تقنية تعيد فيها الأساليب الكائن المستلم (receiver)، مما يسمح بربط استدعاءات متعددة للأساليب معاً في عبارة واحدة. لقد رأيت هذا بالفعل في نمط الباني (Builder pattern)، ولكنه مفهوم مستقل مفيد في سياقات عديدة.
المفتاح هو إرجاع مؤشر إلى الـ struct من كل دالة (method):
type Query struct {
table string
columns []string
where string
limit int
}
func (q *Query) Select(cols ...string) *Query {
q.columns = cols
return q
}
func (q *Query) From(table string) *Query {
q.table = table
return q
}
func (q *Query) Where(condition string) *Query {
q.where = condition
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}الآن يمكنك ربط هذه الاستدعاءات بشكل انسيابي:
query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)يُقرأ هذا تقريبًا مثل اللغة الطبيعية مقارنةً بالبيانات المنفصلة. يعمل تسلسل الأساليب (Method chaining) بشكل أفضل عندما تقوم العمليات بتهيئة أو تعديل كائن دون الحاجة إلى إرجاع قيم أخرى. ويُستخدم بشكل شائع في بناة الاستعلامات (query builders)، وواجهات برمجة تطبيقات التكوين (configuration APIs)، ومكتبات تأكيد الاختبارات (test assertion libraries).
تذكر أن كل تابع (method) يجب أن يستخدم مستقبل مؤشر (pointer receiver) ويعيد *StructType لكي تعمل السلسلة (chain) بشكل صحيح.
التحدي
سهللنقم ببناء منشئ عناصر HTML انسيابي (fluent) يستخدم تسلسل الأساليب (method chaining) لإنشاء وسوم HTML مع السمات والمحتوى! هذا تطبيق عملي لتسلسل الأساليب — ستنشئ واجهة برمجة تطبيقات (API) تُقرأ تقريبًا مثل اللغة الطبيعية عند بناء عناصر HTML.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
element.go: حدد منشئ عناصر HTML الخاص بك مع أساليب قابلة للتسلسل.قم بإنشاء هيكل
Elementيخزن اسم الوسم، وخريطة للسمات (سلسلة نصية إلى سلسلة نصية)، والمحتوى الداخلي. قم ببناء هذه الأساليب القابلة للتسلسل والتي يعيد كل منها*Element:NewElement(tag string)- منشئ (constructor) ينشئ عنصرًا جديدًا باسم الوسم المعطى وخريطة سمات مهيأة (فارغة).Attr(key, value string)- يضيف سمة إلى العنصر.ID(id string)- أسلوب ملائم يضبط سمة "id".Class(class string)- أسلوب ملائم يضبط سمة "class".Content(text string)- يضبط المحتوى الداخلي للعنصر.
أضف أسلوب
Render()الذي يعيد سلسلة HTML النصية. يجب أن تظهر السمات بترتيب أبجدي حسب المفتاح. التنسيق هو:<tag attr1="value1" attr2="value2">content</tag>إذا لم تكن هناك سمات، فقم فقط بتقديم
<tag>content</tag>. إذا لم يكن هناك محتوى، فقم بتقديم<tag attr="value"></tag>.main.go: بناء عناصر HTML باستخدام تسلسل الأساليب.اقرأ اسم الوسم، ثم اقرأ عدد العمليات المطلوب تنفيذها. كل عملية هي واحدة مما يلي:
idمتبوعًا بقيمة IDclassمتبوعًا بقيمة classattrمتبوعًا باسم السمة وقيمتها (سطران)contentمتبوعًا بنص المحتوى
قم بتسلسل جميع العمليات معًا بانسيابية، ثم اطبع كود HTML الناتج.
سيتم توفير المدخلات التالية:
- السطر 1: اسم الوسم
- السطر 2: عدد العمليات
- الأسطر التالية: نوع العملية والقيمة (أو القيم)
على سبيل المثال، إذا كان المعطى:
div
3
id
container
class
main-content
content
Hello Worldيجب أن يكون مخرجك:
<div class="main-content" id="container">Hello World</div>وإذا كان المعطى:
a
3
attr
href
https://example.com
attr
target
_blank
content
Click hereيجب أن يكون مخرجك:
<a href="https://example.com" target="_blank">Click here</a>وإذا كان المعطى:
input
2
attr
type
text
attr
placeholder
Enter nameيجب أن يكون مخرجك:
<input placeholder="Enter name" type="text"></input>وإذا كان المعطى:
p
1
content
Simple paragraphيجب أن يكون مخرجك:
<p>Simple paragraph</p>لاحظ كيف يتيح لك تسلسل الأساليب بناء العناصر خطوة بخطوة بطريقة مقروءة. يقوم كل أسلوب بتعديل العنصر وإعادته، مما يسمح باستدعاء الأسلوب التالي بشكل متسلسل ومباشر.
ورقة مرجعية
تسلسل الدوال (Method chaining) هو تقنية تعيد فيها الدوال المستلم (عادةً *StructType)، مما يسمح بربط استدعاءات دوال متعددة معاً في عبارة برمجية واحدة.
يجب أن تستخدم كل دالة مستلم مؤشر (pointer receiver) وتعيد *StructType:
type Query struct {
table string
columns []string
where string
limit int
}
func (q *Query) Select(cols ...string) *Query {
q.columns = cols
return q
}
func (q *Query) From(table string) *Query {
q.table = table
return q
}
func (q *Query) Where(condition string) *Query {
q.where = condition
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}استدعاء الدوال المتسلسلة بسلاسة:
query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)يعمل تسلسل الدوال بشكل أفضل عندما تقوم العمليات بتكوين أو تعديل كائن دون الحاجة إلى إرجاع قيم أخرى. ويشيع استخدامه في بناة الاستعلامات (query builders)، وواجهات برمجة تطبيقات التكوين (configuration APIs)، ومكتبات تأكيد الاختبارات (test assertion libraries).
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// قراءة اسم الوسم
scanner.Scan()
tagName := scanner.Text()
// قراءة عدد العمليات
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// إنشاء العنصر
element := NewElement(tagName)
// معالجة كل عملية
for i := 0; i < numOps; i++ {
scanner.Scan()
operation := scanner.Text()
// TODO: بناءً على نوع العملية، قم بربط الطريقة المناسبة
// العمليات يمكن أن تكون: "id" أو "class" أو "attr" أو "content"
// - لـ "id": اقرأ السطر التالي كقيمة للمعرف (ID)
// - لـ "class": اقرأ السطر التالي كقيمة للفئة (class)
// - لـ "attr": اقرأ السطرين التاليين (اسم الخاصية، ثم قيمتها)
// - لـ "content": اقرأ السطر التالي كنص المحتوى
// استخدم تسلسل الطرق (method chaining) لبناء العنصر بسلاسة
_ = operation // قم بإزالة هذا السطر عند التنفيذ
}
// طباعة كود HTML الناتج
fmt.Println(element.Render())
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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 Pool12مفاهيم OOP المتقدمة
نمط Functional Optionsنمط Builder في Goتسلسل الدوال (Method Chaining)الأسماء المستعارة للأنواع مقابل التعريفاتأساسيات الـ Reflectionنظرة عامة على Code Generation