الوحدات والمزيج (Modules and Mixins)
في الدرس السابق تعلمنا الوراثة وكيف يمكن لصنف أن يرث من صنف آخر. لكن روبي تدعم وراثة فردية فقط - صنف واحد يرث من صنف أب واحد. فماذا لو أردنا مشاركة سلوكيات بين أصناف غير متعلقة؟ هنا تأتي الوحدات (Modules).
ما هي الوحدة (Module)؟
الوحدة هي حاوية لمجموعة من الدوال والثوابت. تختلف عن الصنف في أنها:
- لا يمكن إنشاء كائنات منها (لا يوجد
Module.new) - تُستخدم لتنظيم الشيفرة أو مشاركته بين أصناف مختلفة
إنشاء وحدة بسيطة
استخدامات الوحدات
الوحدات لها استخدامان رئيسيان:
- التنظيم (Namespacing): تجميع أصناف ودوال متعلقة تحت اسم واحد
- المزيج (Mixins): مشاركة سلوكيات بين أصناف مختلفة
الاستخدام الأول: التنظيم (Namespacing)
عندما يكبر المشروع، قد تتشابه أسماء الأصناف. الوحدات تحل هذه المشكلة:
بدون Namespacing (مشكلة)
مع Namespacing (الحل)
الوصول باستخدام ::
نستخدم :: للوصول لمحتويات الوحدة:
الاستخدام الثاني: المزيج (Mixins)
المزيج هو القوة الحقيقية للوحدات! يسمح بمشاركة دوال بين أصناف مختلفة.
المشكلة: أصناف غير متعلقة تحتاج سلوكاً مشتركاً
المشكلة: الطائر ليس طائرة، والطائرة ليست سوبرمان - لا يمكن استخدام الوراثة!
الحل: استخدام Mixin
include vs extend
include: دوال المثيل (Instance Methods)
include يجعل دوال الوحدة دوال مثيل (تُستدعى على الكائنات):
extend: دوال الصنف (Class Methods)
extend يجعل دوال الوحدة دوال صنف (تُستدعى على الصنف نفسه):
استخدام كليهما معاً
مثال عملي: نظام الصلاحيات
الوصول لمتغيرات المثيل
الوحدات يمكنها الوصول لمتغيرات المثيل @ في الصنف الذي تُخلط معه:
استخدام self في الوحدات
ترتيب البحث عن الدوال (Method Lookup)
عند استدعاء دالة، روبي تبحث بهذا الترتيب:
الترتيب:
- الصنف نفسه (
Child) - آخر وحدة مُضمّنة (
B) - الوحدات السابقة بالترتيب العكسي (
A) - الصنف الأب (
Parent) - الأصناف الأساسية (
Object,Kernel,BasicObject)
استدعاء super في الوحدات
الوحدات المُضمّنة في روبي
روبي توفر وحدات جاهزة مفيدة جداً:
Comparable
Enumerable
جدول المقارنة
| الخاصية | include | extend |
|---|---|---|
| نوع الدوال | دوال مثيل | دوال صنف |
| الاستدعاء | object.method | Class.method |
| الاستخدام الشائع | سلوكيات الكائنات | أدوات مساعدة للصنف |
| المفهوم | الوصف | مثال |
|---|---|---|
| Module | حاوية للدوال والثوابت | module Utils |
| Namespace | تنظيم الشيفرة تحت اسم | MyApp::User |
| Mixin | مشاركة سلوكيات | include Flyable |
:: | الوصول لمحتويات الوحدة | Math::PI |
أخطاء شائعة
1. نسيان الفرق بين include و extend
2. محاولة إنشاء كائن من وحدة
3. نسيان :: للوصول للمحتويات
متى نستخدم الوحدات؟
استخدم Namespacing عندما:
- المشروع كبير ويحتاج تنظيماً
- هناك احتمال تعارض أسماء الأصناف
- تريد تجميع أصناف متعلقة
استخدم Mixins عندما:
- أصناف غير متعلقة تحتاج سلوكاً مشتركاً
- لا يمكن استخدام الوراثة (ليست علاقة "is-a")
- تريد إعادة استخدام شيفرة عبر أصناف مختلفة
ملخص
| المفهوم | الوصف |
|---|---|
module Name | إنشاء وحدة |
Module::Class | Namespacing للتنظيم |
include Module | إضافة دوال مثيل (Mixin) |
extend Module | إضافة دوال صنف |
:: | الوصول لمحتويات الوحدة |
ancestors | ترتيب البحث عن الدوال |
تمرين: نظام القدرات
حان وقت التطبيق! في محرر الشيفرة على اليسار:
المطلوب:
-
أنشئ وحدة
Speakableتحتوي على:- دالة
speakتطبع "أتكلم!"
- دالة
-
أنشئ صنف
Robotيتضمن الوحدةSpeakable:- يحتوي على
attr_reader :name - يحتوي على
initialize(name)
- يحتوي على
-
أنشئ صنف
Animalيتضمن الوحدةSpeakable:- يحتوي على
attr_reader :name - يحتوي على
initialize(name)
- يحتوي على
-
أنشئ روبوت اسمه "روبي" وحيوان اسمه "بوبي"
-
اطبع اسم كل كائن ثم استدعِ speak
الناتج المتوقع:
تلميحات:
- ابدأ بتعريف الوحدة:
module Speakable - استخدم
include Speakableداخل كل صنف - لا تنسَ
attr_reader :nameوinitialize(name)
تذكّر: الوحدات أداة قوية لتنظيم الشيفرة ومشاركة السلوكيات. استخدم Namespacing لتنظيم الأصناف، و Mixins لمشاركة الدوال بين أصناف غير متعلقة.
includeللدوال على الكائنات، وextendللدوال على الصنف نفسه. في الدرس القادم سنتعلم معالجة الأخطاء (Error Handling)!