حجب الـ Methods المضمنة
جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 39 من 107.
أحيانًا تحتاج البنية الخارجية إلى توفير تنفيذها الخاص لطريقة يمتلكها النوع المضمّن بالفعل. يُسمّى هذا الحجب، ويتيح لك تجاوز الطرق المُروَّجة بسلوك مخصّص.
عندما تعرّف طريقةً على البنية الخارجية بالاسم نفسه لطريقة مضمنة، تكون الأولوية للطريقة الخارجية:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
الآن عند استدعاء Speak() على Dog، تُستخدم الطريقة الخارجية:
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - طريقة Dog
fmt.Println(d.Animal.Speak()) // Some sound - طريقة Animal
}
إن method الـ embedded لم تختفِ. لا يزال بإمكانك الوصول إليها explicitly من خلال اسم النوع الـ embedded. يكون هذا النمط مفيدًا عندما تريد تخصيص السلوك مع الإبقاء على التنفيذ الأصلي متاحًا، وربما لاستخدامه من داخل method التي تتولى التجاوز:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
يمنحك حجب الاسم تحكّمًا في السلوك الذي يتم كشفه، مع الحفاظ على إمكانية الوصول إلى الأصل من خلال التأهيل الصريح.
التحدي
سهللنُنشئ نظام إشعارات يمكن فيه لقنوات الإشعارات المختلفة تخصيص طريقة تنسيق الرسائل وتسليمها. ستستخدم تظليل method للسماح لقنوات محددة بتجاوز السلوك الافتراضي، مع إمكانية الوصول إلى التنفيذ الأصلي عند الحاجة.
ستنظّم التعليمات البرمجية عبر ثلاثة ملفات:
base.go: أنشئ بنيةBaseNotificationمع حقليTitleوPriority(وكلاهما من نوع string). أضف إليها أسلوبFormat() stringيعيدnotificationبالتنسيق القياسي:[Priority] Title.channels.go: أنشئ نوعين من قنوات الإشعارات يضمّنانBaseNotificationويظلّلان أسلوبFormatبتنفيذاتهما الخاصة:EmailNotificationمع حقلRecipient. يجب أن يعيدFormat()القيمةEmail to [Recipient]: [Priority] TitleSlackNotificationمع حقلChannel. يجب أن يستدعيFormat()الخاص بالأسلوب المضمّنBaseNotification.Format()ويضيف#[Channel]:في بداية النتيجة
main.go: اقرأ تفاصيل الإشعار من الإدخال، وأنشئ كلا النوعين من الإشعارات، وأثبت التظليل بطباعة ما يلي:- الناتج المنسّق لـ
EmailNotification(باستخدام أسلوبه المظلّل) - التنسيق الأساسي لـ
EmailNotification(باستدعاء الأسلوب المضمّن بشكل صريح) - الناتج المنسّق لـ
SlackNotification(الذي يستخدم داخليًا الأسلوب الأساسي)
- الناتج المنسّق لـ
سيتم توفير المدخلات التالية:
- السطر 1: عنوان الإشعار
- السطر 2: مستوى الأولوية
- السطر 3: مستلم البريد الإلكتروني
- السطر 4: اسم قناة Slack
على سبيل المثال، عند إعطاء Server Alert وHIGH وadmin@company.com وops-alerts، يجب أن يكون الناتج كما يلي:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server Alertلاحظ كيف يستبدل EmailNotification تنسيق base بالكامل، بينما يحسّنه SlackNotification باستدعاء الأسلوب الأصلي وإضافة سياق القناة. يوضّح كلا النهجين طرقًا مختلفة لاستخدام تظليل method بفعالية.
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// اقرأ عنوان الإشعار
scanner.Scan()
title := scanner.Text()
// اقرأ مستوى الأولوية
scanner.Scan()
priority := scanner.Text()
// اقرأ مستلم البريد الإلكتروني
scanner.Scan()
recipient := scanner.Text()
// اقرأ اسم قناة Slack
scanner.Scan()
channel := scanner.Text()
// TODO: أنشئ EmailNotification مع الإشعار الأساسي و title و priority و recipient
// TODO: أنشئ SlackNotification مع الإشعار الأساسي و title و priority و channel
// TODO: اطبع المخرجات المنسقة لـ EmailNotification (باستخدام method المظلل الخاص به)
// TODO: اطبع التنسيق الأساسي لـ EmailNotification (باستدعاء الـ method المضمّن صراحةً)
// TODO: اطبع المخرجات المنسقة لـ SlackNotification
fmt.Println() // عنصر نائب - أزله عند التنفيذ
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت