أهلاً بيك يا صديقي في الدرس الثاني من سلسلة المعمارية المتقدمة لإضافات ووردبريس. النهاردة هنتكلم عن مبدأ من أعمق وأهم مبادئ هندسة البرمجيات كائنية التوجه (OOP): **تفضيل التركيب على الوراثة (Composition over Inheritance)**، ومحاربة وحش **الاعتماد الخفي (Hidden Coupling)** اللي بيسمم كود الإضافات.
أول ما أي مبرمج بيتعلم البرمجة كائنية التوجه (OOP)، بيقع في غرام كلمة extends؛ تلاقيه فرحان إنه بيعمل كلاس ويورث منه كلاس تاني، والتاني يورث للتالت، وتلاقي شجرة عائلة كاملة من الكلاسات! لكن في مشاريع ووردبريس الحقيقية، الوراثة العميقة بتتحول لـ “فخ موت” تقني؛ أي تعديل في الكلاس الأب بيكسر 15 كلاس ابن بدون ما تاخد بالك، وتلاقي نفسك محمل في الذاكرة كائنات عملاقة أنت مش محتاج منها غير دالة واحدة بس!
في المقال ده، هنشرح بالعامية المصرية التقنية إزاي تفكر بعقلية الـ **Composition**، وإزاي تبني كلاسات مرنة بتعتمد على علاقة “يمتلك” (Has-a) بدل علاقة “هو” (Is-a)، وإزاي تحول الاعتمادات الخفية (Hidden Dependencies) لاعتمادات صريحة وواضحة يسهل اختبارها وعزلها، مع كود إنتاجي متكامل لنظام إشعارات ذكي، ودراسة حالة واقعية من سوق الشحن المصري و20 سؤال وجواب لتثبيت الفكرة تماماً.
معضلة الجوريلا والموزة: كوارث الوراثة العميقة في ووردبريس
مهندس البرمجيات الشهير “جو أرمسترونج” (مخترع لغة Erlang) لخص مشكلة الوراثة في مقولة تاريخية قال فيها:
“المشكلة في اللغات الكائنية هي أنك لما بتطلب موزة، بتاخد معاها غوريلا ماسكة الموزة ومعاها الغابة كلها اللي بتعيش فيها الغوريلا!”
في ووردبريس، السيناريو ده بيتكرر كل يوم؛ تلاقي مطور عامل كلاس اسمه BasePlugin فيه اتصال بقاعدة البيانات، وتسجيل للهوكات، واستدعاء لإعدادات الإضافة، ومعالجة رسائل البريد. وبعدين يجي يعمل كلاس صغير وظيفته طباعة باركود الفاتورة، فيعمل:
class InvoiceBarcode extends BasePlugin
الكارثة هنا إن كائن الباركود البسيط ده ورث معاه كل المتغيرات والاتصالات والوظائف المعقدة اللي في الكلاس الأب، وبقى معتمد اعتماد أعمى على البنية الداخلية للأب (Tight Coupling). لو المطور غير اسم متغير في الأب أو حذف دالة، كلاس الباركود هيقع فوراً، ودي الظاهرة الهندسية المعروفة باسم The Fragile Base Class Problem.
| وجه المقارنة | الوراثة الكلاسيكية (Inheritance) | التركيب المعماري (Composition) |
|---|---|---|
| نوع العلاقة | علاقة “هو نوع من” (Is-a relationship). الكلب هو حيوان. | علاقة “يمتلك أو يستخدم” (Has-a / Uses-a relationship). السيارة تمتلك محرك. |
| درجة الترابط (Coupling) | ترابط وثيق جداً (Tight Coupling)؛ الابن يعرف تفاصيل تنفيذ الأب. | ترابط مفكك ورشيق (Loose Coupling)؛ الكائن يتعامل مع واجهات (Interfaces). |
| المرونة وقت التشغيل (Runtime Flexibility) | ثابتة ولا يمكن تغييرها أثناء تشغيل البرنامج (Compile-time). | ديناميكية؛ يمكن استبدال الأجزاء وتغيير السلوك أثناء تشغيل الريكوست. |
| قابلية التوسع | معقدة؛ تؤدي لانفجار كلاسات الوراثة المتعددة والتكرار. | عالية جداً؛ تجمع الكائنات كقطع الليجو (Lego Bricks) لبناء وظائف جديدة. |
| سهولة الاختبار (Unit Testing) | شبه مستحيلة بدون بناء أو محاكاة الكلاس الأب بكل تبعياته. | في غاية السهولة عبر حقن كائنات بديلة وهمية (Mock Objects). |
الاعتماد الخفي (Hidden Coupling): السم الصامت في كود ووردبريس
إيه هو “الاعتماد الخفي”؟ الاعتماد الخفي هو إن الكلاس بتاعك يحتاج خدمات خارجية عشان يشتغل، لكنه بيخبي الحاجة دي جواه، ومبيعلنهاش صراحة في الـ Constructor بتاعه!
تعال نشوف المثال الكارثي ده:
sendNotification(...);
}
}
لو أنت مبرمج بتستخدم الكلاس ده، وبصيت على الدالة processOrder(int $orderId)، هتفتكر إنها بتاخد رقم الطلب وبس. مفيش أي إشارة إلى إنها محتاجة جدول ووردبريس، ومحتاجة مفتاح API في قاعدة البيانات، ومحتاجة كلاس Twilio يكون موجود في الذاكرة!
لو حاولت تختبر الكلاس ده باختبار وحدة، هيفشل لأن المتغيرات والدوال دي مش موجودة خارج بيئة ووردبريس.
العلاج الهندسي: التصريح بالاعتمادات عبر المنشئ (Constructor Injection)
في المعمارية النظيفة، الكلاس بيعلن بصوت عالي وبكل صراحة عن كل الأدوات اللي محتاجها عشان يشتغل:
db->findOrder($orderId);
$this->smsSender->send($order->phone, 'تم تأكيد طلبك بنجاح');
}
}
أنماط التركيب العملية (Composition Design Patterns)
التركيب مش مجرد كلمة بنقولها، ده منهج بيتحقق من خلال أنماط تصميمية عالمية معتمدة:
1. نمط الاستراتيجية (Strategy Pattern)
تخيل إن إضافتك بتدعم وسائل دفع أو بوابات شحن متعددة في مصر (زي فوري، كاش كول، فودافون كاش، وتابي). بدل ما تعمل كلاس أب وتورث منه، بنعمل واجهة موحدة PaymentGatewayInterface، ونخلي الكلاس المسؤول عن إتمام الطلبات يستقبل البوابة المختارة كقطعة تركيبية قابلة للاستبدال!
2. نمط الزخرفة (Decorator Pattern)
لو عايز تضيف ميزة جديدة لكائن موجود بدون ما تعدل كوده وبدون وراثة، بتلفه داخل كلاس مغلف (Wrapper) بيضيف الميزة دي؛ زي مثلاً إضافة كاش مؤقت (Cache Decorator) حول مستودع البيانات.
3. نمط التفويض (Delegation Pattern)
الكائن الرئيسي بيستقبل الطلب، وبدل ما ينفذه بنفسه أو يورثه، بيفوض تنفيذه لكائن داخلي متخصص (Helper / Delegate).
التطبيق البرمجي العملي: محرك إشعارات ذكي بنمط التركيب الصريح
تعال نبني كود إنتاجي يوضح إزاي نبني نظام إرسال إشعارات مرن بيدعم الإيميل والرسائل القصيرة (SMS) وبوت تليجرام، باستخدام مبادئ Composition و Dependency Injection الخالية تماماً من الاعتمادات الخفية:
1. تعريف واجهات العقود (Interfaces)
<?php
declare(strict_types=1);
namespace EnterpriseLogisticsNotification;
interface NotificationChannelInterface {
public function send(string $recipient, string $message): bool;
public function getChannelName(): string;
}
2. كلاسات القنوات التركيبية (Channels)
apiKey)) {
return false;
}
// استدعاء بوابة SMS بواسطة wp_remote_post
$response = wp_remote_post('https://api.sms-provider.com.eg/send', [
'body' => json_encode([
'api_key' => $this->apiKey,
'sender' => $this->senderId,
'mobile' => $recipient,
'text' => $message
]),
'headers' => ['Content-Type' => 'application/json'],
'timeout' => 5
]);
return !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200;
}
public function getChannelName(): string {
return 'SMS';
}
}
3. الموزع المركزي المركب (NotificationDispatcher via Composition)
channels[$channel->getChannelName()] = $channel;
}
/**
* إرسال الإشعار عبر جميع القنوات المركبة
*/
public function dispatch(string $recipient, string $message): array {
$results = [];
foreach ($this->channels as $name => $channel) {
$results[$name] = $channel->send($recipient, $message);
}
return $results;
}
}
4. كلاس خدمة الطلبات المستهلك للنظام (OrderNotificationService)
dispatcher->dispatch($customerEmail, $msg);
$this->dispatcher->dispatch($customerPhone, $msg);
}
}
دراسة حالة حقيقية: إنقاذ منصة شحن لوجستي بالمعادي
تعال أحكي لك واقعة هندسية حصلت مع منصة شحن لوجستي مقرها في المعادي بالقاهرة، بتدير أسطول سيارات شحن لأكتر من 400 متجر تجارة إلكترونية في مصر:
الوضع المأساوي القديم:
مبرمج الإضافة القديم كان عامل كلاس ضخم اسمه LogisticsBaseCarrier، وكل شركة شحن تانية كان بيورث منها:
BostaCarrier extends AramexCarrier extends LogisticsBaseCarrier
الوراثة كانت ماشية في سلسلة هزلية ومرعبة! الكلاس الأخير كان بيورث دوال من كلاسات ملغية تماماً.
لحظة الكارثة:
شركة الشحن الأولى (أرامكس) غيرت الـ API بتاعها وطريقة حساب أسعار المحافظات (القاهرة الكبرى، الدلتا، الصعيد). المبرمج دخل عدل دالة حساب الأسعار في الكلاس الأب LogisticsBaseCarrier.
وفوراً، حصل الانهيار: كلاس بوسطة وكلاس الشحن الداخلي وكلاس البريد المصري اتأثروا بالتعديل دا، وبدأت الإضافة تحسب الشحن للعملاء في أسوان والإسكندرية بـ 0 جنيه! المنصة فضلت شغالة كده لمدة 48 ساعة، وخسرت الشركة أكتر من 260,000 جنيه مصري في تكاليف شحن دفعوها من جيبهم لشركات التوصيل!
العلاج المعماري بنمط Composition:
لما مسكنا المشروع:
- هدمنا سلسلة الوراثة تماماً: لغينا كل
extendsبين شركات الشحن. - بنينا واجهة موحدة
CarrierInterface: كل شركة شحن بقت كلاس مستقل تماماً بينفذ واجهة موحدة فيهاcalculateRate()وcreateWaybill(). - بناء محرك الشحن المركب
ShippingRateEngine: الكائن المركزي بقى بياخد شركة الشحن كقطعة غيار محقونة (Injected Strategy). لو شركة غيرت الـ API بتاعها، بنعدل الكلاس بتاعها لوحده في أمان تام، ومستحيل يتأثر أي كلاس تاني بسطر كود واحد!
النتيجة: بعد التحول للتركيب، قدرت الشركة تضيف شركتين شحن جدد (Mylerz و J&T) في يومين عمل فقط، بدون ما يلمسوا أي سطر كود قديم في المنصة، وبنسبة أخطاء صفرية.
الوصايا العشر لتطبيق Composition ومحاربة الاعتماد الخفي
- اسأل نفسك أولاً: هل الكلاس يمثل جوهر الشيء (Is-a) أم أنه مجرد وظيفة يستخدمها (Has-a)؟ لو التانية يبقى Composition فوراً.
- احذر عمق الوراثة: إذا تجاوز عمق شجرة الوراثة لديك مستويين (Parent -> Child)، فأنت في منطقة خطر معماري ويجب إعادة الهيكلة.
- امنع الاعتماد الخفي: لا تستدعِ متغيرات عامة أو كائنات جديدة داخل دوالك، بل مررها بصراحة في الـ Constructor.
- اعتمد على العقود والواجهات (Interfaces): لا تعتمد على كلاسات محددة (Concrete Classes)، بل اجعل كودك يتعامل مع واجهات لتسهيل الاستبدال.
- وفر كائنات وهمية (Mocks) للاختبار: الاعتماد الصريح يسمح لك باختبار كودك بدون الحاجة لقواعد بيانات حقيقية أو إنترنت.
- تجنب كلاسات Base الكاذبة: لا تنشئ كلاس اسمه
BasePluginوتضع فيه كل ما يطرأ على بالك، فهذا أسرع طريق لكود الاسباجيتي. - استخدم نمط الاستراتيجية (Strategy Pattern): عندما تتعدد خوارزميات التنفيذ (دفع، شحن، تصدير)، اجعل كل خوارزمية كائناً مستقلاً يُحقن عند الحاجة.
- استفد من ميزات PHP 8+: استخدم ميزة Constructor Promotion والـ Readonly Properties لكتابة كود حقن اعتمادات رشيق وأنيق.
- احذر من التكلفة الخفية للكائنات غير المستخدمة: الوراثة تجبرك على حجز ذاكرة لكل خصائص الأب، بينما التركيب يسمح لك بتحميل ما تحتاجه فقط.
- وثق اعتماداتك: اجعل رأس كل ملف كلاس واضحاً في معالمه وما يعتمد عليه ليتمكن زملاؤك في الفريق من فهم حدوده وسياق عمله.
نمط التزيين (Decorator Pattern): إضافة الكاش واللوجات دون وراثة
واحدة من أعظم تطبيقات التركيب هي **نمط التزيين (Decorator Pattern)**. تخيل إن عندك خدمة بتجلب أسعار المنتجات أو بيانات الشحنات من API خارجي، والخدمة دي شغالة ومستقرة تماماً. جه مدير المشروع وقال لك: “عايزين نضيف نظام كاش مؤقت (Caching) على استجابات الـ API عشان نوفر استهلاك الرصيد، وعايزين نسجل لوج زمني لكل طلب بيحصل!”.
المطور التقليدي هيعمل إيه؟ هيروح يعمل كلاس جديد يورث من الخدمة القديمة:
class CachedShippingService extends ShippingService
ويعمل Override للدوال ويحط جواها الكاش، ولو طلبوا لوج هيعمل:
class LoggedCachedShippingService extends CachedShippingService
ودي بداية الانهيار المعماري وتكدس الكلاسات!
أما مطور المعمارية النظيفة، هيستخدم الـ Decorator Pattern عبر الـ Composition. إحنا بنعمل كلاس بيغلف الخدمة الأصلية (Wraps the original service) وبينفذ نفس الواجهة (Interface)، بالشكل العملي ده:
innerCalculator->getRate($destination, $weight);
set_transient($cacheKey, $rate, $this->cacheTtl);
return $rate;
}
}
// كلاس التزيين باللوج (Logging Decorator)
final class LoggedRateCalculator implements RateCalculatorInterface {
public function __construct(
private readonly RateCalculatorInterface $innerCalculator
) {}
public function getRate(string $destination, float $weight): float {
$startTime = microtime(true);
$rate = $this->innerCalculator->getRate($destination, $weight);
$duration = microtime(true) - $startTime;
error_log(sprintf('[LOGISTICS LOG] Rate for %s (weight: %.2f) is %.2f. Time: %.4fs', $destination, $weight, $rate, $duration));
return $rate;
}
}
شايف السحر المعماري هنا؟ تقدر تركب الخدمات دي زي المكعبات حسب الرغبة وقت التشغيل:
getRate('Cairo', 2.5);
هل الـ Traits في PHP تغني عن التركيب (Composition)؟
كتير من المطورين بيفتكروا إن الـ Traits هي الحل السحري لتجنب الوراثة المتعددة، ويبدأ يعمل use LoggerTrait, DatabaseTrait, ValidatorTrait في كل كلاس يعمله.
لكن الحقيقة الهندسية إن الـ Traits في PHP ليست إلا عملية **نسخ ولصق برمجية وقت الترجمة (Compiler-assisted Copy-Paste)**! استخدام الـ Traits العشوائي له عيوب خطيرة:
- إخفاء التبعيات (Hidden Dependencies): لو الـ Trait بتستخدم متغيرات خاصة أو دوال ووردبريس، الكلاس اللي هيستخدمها هيتلوث بنفس الاعتمادات الخفية.
- غياب الواجهات (No Interfaces for Traits): لا يمكنك فرض عقد (Contract) محدد على Trait، ولا يمكنك استبدال سلوكها بكائن وهمي (Mock) أثناء الـ Unit Testing.
- تشتت المسؤوليات: الكلاس اللي فيه 5 Traits بيتحول لكائن عملاق في ثوب متنكر ينتهك مبدأ المسؤولية الواحدة (SRP).
القاعدة الهندسية: استخدم الـ Traits فقط للوظائف المساعدة عديمة الحالة (Stateless Utility Methods)، زي تنسيق التاريخ أو تشفير النصوص، أما أي خدمة حقيقية لها حالة (State) أو تتواصل مع قواعد بيانات وخدمات خارجية، فمكانها الطبيعي هو **خدمة مستقلة محقونة بنمط التركيب (Injected Service via Composition)**.
دليل خطوة بخطوة: إعادة هيكلة كود وراثة معقد إلى تركيب رشيق
لو استلمت إضافة قديمة مليانة كلاسات وراثة متداخلة وعايز تنظفها بأمان بدون ما توقف الموقع، اتبع الخطة الهندسية دي:
- حدد الواجهة الأساسية (Extract Interface): شوف الكلاس الأب كان بيوفر إيه، واكتب Interface فيها الدوال الأساسية التي تحتاجها بقية أجزاء النظام.
- استخرج الخدمات المتخصصة (Extract Collaborator Classes): انقل عمليات الاتصال بقاعدة البيانات لكلاس Repository، وعمليات إرسال الإيميلات لكلاس Mailer، وعمليات التحقق لكلاس Validator.
- حول كلاسات الأبناء إلى مستهلكين مستقلين: استبدل كلمة
extends BaseClassبـimplements Interface، واحقن الخدمات المتخصصة في الـ Constructor. - حذف الكلاس الأب تدريجياً: بعد ما تفضي كل محتويات الكلاس الأب وتتأكد إن مفيش كائن بيورث منه، تقدر تحذفه وأنت مطمئن ورايق البال.
مبدأ عكس الاعتماد (Dependency Inversion Principle – DIP) في ووردبريس
عشان تكتمل الصورة الذهنية للتركيب، لازم تفهم المبدأ الخامس والأخير من مبادئ SOLID الشهيرة: **مبدأ عكس الاعتماد (DIP)**.
المبدأ ده بينص ببساطة على قاعدتين ذهبيتين:
- الوحدات عالية المستوى (High-level modules) لا يجب أن تعتمد على الوحدات منخفضة المستوى (Low-level modules). كلاهما يجب أن يعتمد على التجريد (Abstractions).
- التجريدات لا يجب أن تعتمد على التفاصيل. التفاصيل هي التي يجب أن تعتمد على التجريدات.
في عالم ووردبريس، “الوحدة عالية المستوى” هي خدمة معالجة الطلبات في متجرك (Order Processing Service). و”الوحدة منخفضة المستوى” هي كود الاتصال ببوابة الدفع فوري أو ميزا.
لو خليت كلاس الطلبات يعتمد مباشرة على كلاس فوري بـ extends FawryGateway أو بإنشاء كائن مباشر new FawryGateway()، فأنت كسرت المبدأ وخليت قلب بيزنس شركتك مقترن ببوابة دفع تجارية ممكن تتغير في أي وقت!
أما لو بنيت واجهة مجردة اسمها PaymentGatewayInterface وخليت كلاس الطلبات يستقبل هذه الواجهة عبر الـ Composition، وخليت كلاس فوري ينفذ هذه الواجهة، فأنت عكست اتجاه الاعتماد تماماً! دلوقتي فوري هي اللي بتخضع لقواعد نظامك، وبكرة الصبح تقدر تبدلها بـ Paymob أو Stripe أو أي بنك محلي في نصف ساعة بدون لمس سطر كود واحد في معالجة طلبات متجرك.
مقارنة عملية في اختبارات الوحدة: كود الوراثة المستحيل مقابل التركيب السهل
تعال نشوف الفرق العملي لما تيجي تكتب اختبار وحدة (Unit Test) لكود مكتوب بنمط الوراثة والاعتماد الخفي مقابل كود مكتوب بالتركيب والاعتماد الصريح.
في كود الوراثة، عشان تختبر دالة بتخصم 10% على طلب، بتلاقي نفسك مضطر تعمل:
apply(100);
}
أما مع كود التركيب والاعتماد الصريح، الاختبار بياخد 4 سطور وبيشتغل في 2 مللي ثانية بدون ما تشغل ووردبريس أصلاً:
createMock(NotificationDispatcher::class);
// 2. التحقق من أن دالة الإرسال سيتم استدعاؤها مرتين (للإيميل والموبايل)
$mockDispatcher->expects($this->exactly(2))
->method('dispatch')
->withConsecutive(
['customer@example.com', $this->stringContains('شحنتك رقم #1024')],
['01012345678', $this->stringContains('شحنتك رقم #1024')]
);
// 3. حقن الكائن الوهمي في الخدمة عبر الـ Composition
$service = new OrderNotificationService($mockDispatcher);
// 4. تنفيذ الدالة واختبار السلوك في بيئة معزولة ونقية 100%
$service->notifyCustomerAboutDispatch(1024, '01012345678', 'customer@example.com');
}
}
شايف الفرق؟ ده السر اللي بيخلي كبرى الشركات التقنية تطلب مهندسين فاهمين Composition و Clean Architecture؛ لأن الكود ده هو الوحيد اللي ينفع يتعمل عليه أنظمة الاختبار الآلي المستمر (CI/CD Pipelines) وتضمن سلامته مع كل Commit قبل ما يوصل للإنتاج.
الأسئلة الشائعة حول Composition والبرمجة الكائنية في ووردبريس (FAQ)
1. ما معنى العبارة الشهيرة “Composition over Inheritance”؟
تعني تفضيل بناء الكلاسات عبر تجميع واستخدام كائنات وخدمات صغيرة مستقلة (علاقة امتلاك Has-a)، بدلاً من وراثة كلاسات كبيرة ومعقدة (علاقة Is-a) تؤدي لترابط شديد وهشاشة في الكود.
2. ما هي المشكلة الرئيسية في استخدام الوراثة في إضافات ووردبريس الكبيرة؟
المشكلة تكمن في “هشاشة الكلاس الأب” (Fragile Base Class)؛ حيث إن أي تعديل أو إصلاح في الكلاس الأب قد يؤدي لكسر سلوك العشرات من الكلاسات الأبناء بشكل غير متوقع.
3. ما هو “الاعتماد الخفي” (Hidden Dependency) ولماذا هو خطير؟
هو استدعاء خدمات خارجية (مثل global $wpdb أو دوال get_option() أو إنشاء كائنات جديدة بـ new) داخل دوال الكلاس دون إعلانها في المنشئ، مما يخفي متطلبات تشغيل الكود ويمنع اختباره آلياً.
4. كيف أكتشف أن كودي يعاني من اعتماد خفي؟
إذا حاولت استخدام الكلاس في بيئة اختبار خارجية (Unit Test) ووجدته يطلب دوال ووردبريس أو يرمي خطأ أن كائناً ما غير معرف، فهذا دليل قاطع على وجود اعتمادات خفية.
5. ما هو الحل الهندسي الموصى به للتخلص من الاعتماد الخفي؟
استخدام تقنية “حقن التبعيات عبر المنشئ” (Constructor Dependency Injection)، وتمرير كافة الكائنات والخدمات المطلوبة كمعاملات للمنشئ.
6. متى تكون الوراثة (Inheritance) مناسبة ومبررة؟
عندما تكون العلاقة حقيقية وقطعية بأن الابن “هو نوع من” الأب دون زيادة أو نقصان (مثل class AdminNotice extends Notice)، ولا تتغير هذه العلاقة إطلاقاً أثناء التشغيل.
7. كيف يساهم نمط التركيب في تسهيل اختبارات الوحدة (Unit Testing)؟
لأنه يتيح لك أثناء الاختبار استبدال القنوات والخدمات الحقيقية (مثل بوابات الرسائل أو قواعد البيانات) بكائنات تجريبية وهمية (Mocks) لا تكلف وقتاً ولا تجري اتصالات حقيقية.
8. ما هو نمط الاستراتيجية (Strategy Pattern) وكيف يرتبط بالتركيب؟
هو نمط يسمح بتعريف عائلة من الخوارزميات وتغليف كل منها داخل كلاس ينفذ واجهة موحدة، ثم حقن الاستراتيجية المناسبة داخل الكائن الرئيسي وقت التشغيل كقطعة تركيبية.
9. هل يعني استخدام Composition كتابة كود أطول من الوراثة؟
قد يتطلب كتابة واجهات وكلاسات إضافية في البداية، لكنه يوفر مئات الساعات في الصيانة، ويمنع تكرار الكود، ويسهل إضافة الميزات الجديدة في دقائق دون كسر القديم.
10. كيف يؤثر نمط Composition على استهلاك ذاكرة السيرفر؟
يحسن استهلاك الذاكرة لأن النظام يحمل في الذاكرة فقط الكائنات والخدمات التي يحتاجها الريكوست الحالي، بدلاً من تحميل أشجار الوراثة العملاقة بكل ما تحتويه.
11. ما فائدة استخدام ميزة Constructor Property Promotion في PHP 8؟
تسمح بتعريف وتمرير الخصائص المحقونة مباشرة داخل رأس المنشئ دون الحاجة لكتابة إعلانات الخصائص وتعيينها يدوياً، مما يجعل كود الـ Composition فائق الأناقة والاختصار.
12. هل يمنع التركيب استخدام دوال ووردبريس الأساسية تماماً؟
لا، بل ينظم استخدامها؛ حيث يتم تغليف دوال ووردبريس في كلاسات خدمات أو محولات (Adapters) محقونة، بدلاً من بعثرتها عشوائياً في كل ركن من أركان الكود.
13. ما هو مبدأ Interface Segregation Principle وعلاقته بالتركيب؟
هو المبدأ الرابع من مبادئ SOLID، وينص على تقسيم الواجهات الكبيرة إلى واجهات صغيرة ودقيقة، بحيث لا يُجبر الكائن المركب على تنفيذ دوال لا يحتاجها.
14. كيف يتم استبدال الكلاس الأب العملاق (God Object) في إضافة موجودة؟
عن طريق تفكيك وظائفه تدريجياً إلى خدمات صغيرة (مثل Logger, Mailer, DatabaseRepository)، ثم حقن هذه الخدمات داخل الكلاس القديم عبر Composition حتى يتم التخلص منه تماماً.
15. ماذا لو احتجت لتطبيق سلوك مشترك بين كلاسات غير مرتبطة جينياً؟
استخدم نمط التركيب عبر حقن خدمة مشتركة في كلا الكلاسين، أو استخدم Traits في PHP كحل تركيبي محدود للوظائف المساعدة البسيطة.
16. كيف يساعد Composition في العمل الجماعي بين عدة مطورين؟
يتيح للمطورين العمل على أجزاء مختلفة من النظام في وقت واحد؛ فمطور يبني بوابة دفع جديدة ومطور يبني بوابة رسائل جديدة طالما أن كلاهما ملتزم بعقد الواجهة (Interface).
17. ما هو الفرق بين التجميع (Aggregation) والتركيب (Composition) الدقيق؟
في الـ Composition الدقيق، إذا تدمر الكائن الرئيسي تتدمر الأجزاء التابعة له، أما في الـ Aggregation، فالأجزاء المحقونة يمكن أن تعيش وتُستخدم بشكل مستقل خارج الكائن الرئيسي.
18. هل يدعم نمط التركيب التبديل الديناميكي للخدمات وقت التشغيل؟
نعم، بكل سهولة؛ يمكنك تغيير القناة أو بوابة الدفع أثناء تنفيذ الريكوست بمجرد استدعاء دالة تعيين (Setter) أو تمرير كائن مختلف في حلقة التكرار.
19. كيف يؤثر التخلص من الاعتمادات الخفية على سرعة اكتشاف الـ Bugs؟
يجعل تتبع مسار البيانات في منتهى الوضوح؛ فأنت تعرف بدقة من أين جاءت كل معلومة وأي كائن مسؤول عن معالجتها دون مفاجآت من دوال مخفية في الخلفية.
20. ما هي النصيحة الأهم لمطور ووردبريس يريد احتراف المعمارية الكائنية؟
“فكك مشكلتك إلى أدوار صغيرة متخصصة، واجعل كل كلاس يركز في مهنة واحدة، وركبهم معاً كفريق عمل متناغم؛ هذا هو سر البرمجيات التي تعيش وتكبر لسنوات دون أن تنهار!”
أين نرى مبدأ التركيب (Composition) داخل نواة ووردبريس نفسها؟
على الرغم من أن ووردبريس يمتلك أجزاء قديمة تعتمد على الدوال الإجرائية والوراثة، إلا أن التطور الحديث في نواة ووردبريس (WordPress Core) أظهر بوضوح الانتقال لمبدأ التركيب. على سبيل المثال، في كلاسات معالجة واجهات برمجة التطبيقات WP_REST_Server ومحركات الكتل البرمجية Block Editor (Gutenberg)، نجد أن النواة تقوم بتسجيل الكنترولرز والموزعات كمكونات محقونة ومفصولة تماماً، حيث يتم تفويض كل طلب لمتحكم متخصص دون الحاجة لوراثة كلاسات عملاقة، وهو نفس النمط الذي نتبناه في تطوير الإضافات المتقدمة.
المصادر والمراجع الرسمية
- Wikipedia: Composition over Inheritance Principles
- PHP Manual: Object-Oriented Programming & Interfaces
- Refactoring Guru: Strategy Pattern & Behavioral Composition
- WordPress Developer Handbook: Object-Oriented Plugin Development
- Martin Fowler: Inversion of Control Containers and the Dependency Injection Pattern


اترك تعليقاً
يجب أنت تكون مسجل الدخول لتضيف تعليقاً.