التركيب مقابل الوراثة
جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 56 من 91.
في الفصل السابق، تعلمت أن السمات (traits) توفر إمكانية "has-a" بينما ينشئ التوريث (inheritance) علاقة "is-a". يأخذ التركيب (Composition) مفهوم "has-a" إلى أبعد من ذلك من خلال بناء كائنات معقدة من كائنات أبسط، وتخزينها كخصائص بدلاً من التوريث منها.
مع الوراثة، قد يقوم Car بتوسيع Vehicle. ومع التركيب، فإن Car يحتوي على كائن Engine:
<?php
class Engine {
public function start(): string {
return "Engine started";
}
}
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine();
}
public function start(): string {
return $this->engine->start();
}
}
$car = new Car();
echo $car->start();
الناتج:
Engine startedلا ترث فئة Car من Engine - بل تمتلك واحداً. يوفر هذا النهج مزايا كبيرة: يمكنك استبدال المحرك بتنفيذ مختلف، وتظل الفئات مفككة الارتباط، وتتجنب التسلسلات الهرمية الجامدة التي يخلقها التوريث العميق.
من المبادئ التوجيهية الشائعة في البرمجة كائنية التوجه (OOP) هي "تفضيل التركيب على الوراثة." تعمل الوراثة بشكل جيد مع التسلسلات الهرمية الحقيقية للأنواع (فالـ Dog هو حقاً Animal)، لكن التركيب يتألق عندما تقوم بتجميع الوظائف من أجزاء مستقلة. يمكنك دمج كائنات متعددة، واستبدالها في وقت التشغيل، واختبار المكونات بشكل منفصل.
يستكشف الدرس التالي حقن التبعية (dependency injection)، والذي يعتمد بشكل مباشر على التركيب (composition) من خلال التحكم في كيفية تلقي الكائنات لتبعياتها.
التحدي
سهللنقم ببناء نظام كمبيوتر باستخدام التركيب (composition)، حيث يتم تجميع Computer من كائنات مكونات منفصلة بدلاً من الوراثة منها. يوضح هذا كيف يسمح لك التركيب ببناء كائنات معقدة من أجزاء أبسط ومستقلة.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
Processor.php— أنشئ فئةProcessorتمثل وحدة المعالجة المركزية (CPU). استخدم ترويج المعاملات في المنشئ (constructor promotion) لتعريف$model(string) خاص و$cores(integer) خاص. أضف طريقةgetSpecs()التي تعيد"[model] ([cores] cores)".Memory.php— أنشئ فئةMemoryتمثل ذاكرة الوصول العشوائي (RAM). استخدم ترويج المعاملات في المنشئ لتعريف$size(integer يمثل الجيجابايت) خاص. أضف طريقةgetCapacity()التي تعيد"[size]GB RAM".main.php— قم بتضمين ملفي المكونات وأنشئ فئةComputerتستخدم التركيب. يجب علىComputerأن:- تخزن كائن
ProcessorوكائنMemoryكخصائص خاصة (private properties) - تقبل طراز المعالج، وعدد الأنوية، وحجم الذاكرة في المنشئ الخاص بها
- تنشئ كائنات المكونات داخلياً أثناء عملية الإنشاء
- تمتلك طريقة
describe()تعيد"Computer with [processor specs] and [memory capacity]"
ستتلقى ثلاثة مدخلات: طراز المعالج، وعدد الأنوية، وحجم الذاكرة بالجيجابايت. أنشئ
Computerبهذه المواصفات واطبع نتيجة استدعاءdescribe().- تخزن كائن
لاحظ كيف أن Computer لا يرث من Processor أو Memory — بل هو يحتويهما. هذه العلاقة من نوع "has-a" من خلال التركيب تحافظ على استقلالية كل مكون وقابليته لإعادة الاستخدام، مع السماح لـ Computer بتفويض المهام إلى أجزائه عند الحاجة.
ورقة مرجعية
يقوم التركيب (Composition) ببناء كائنات معقدة من كائنات أبسط عن طريق تخزينها كخصائص بدلاً من الوراثة منها. يؤدي هذا إلى إنشاء علاقة "يحتوي على" (has-a) حيث تحتوي فئة واحدة على مثيلات من فئات أخرى.
مثال أساسي على التركيب:
<?php
class Engine {
public function start(): string {
return "Engine started";
}
}
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine();
}
public function start(): string {
return $this->engine->start();
}
}لا ترث الفئة Car من Engine - بل تمتلك واحداً. هذا يحافظ على الفئات مفككة الارتباط (loosely coupled) ويتجنب التسلسلات الهرمية الصارمة للوراثة.
تفضيل التركيب على الوراثة: تعمل الوراثة بشكل جيد مع التسلسلات الهرمية الحقيقية للأنواع (الـ Dog هو Animal)، لكن التركيب يتألق عند تجميع الوظائف من أجزاء مستقلة. يمكنك دمج كائنات متعددة، واستبدالها في وقت التشغيل، واختبار المكونات بشكل منفصل.
جرّب بنفسك
<?php
require_once 'Processor.php';
require_once 'Memory.php';
class Computer
{
// TODO: تعريف خصائص خاصة لتخزين كائنات المعالج (Processor) والذاكرة (Memory)
// TODO: يجب أن يقبل المنشئ (Constructor) طراز المعالج، وعدد الأنوية، وحجم الذاكرة
// وإنشاء كائنات المكونات داخلياً
public function __construct()
{
// TODO: إنشاء كائنات المعالج والذاكرة وتخزينها
}
// TODO: تنفيذ دالة describe() التي تعيد
// "Computer with [processor specs] and [memory capacity]"
public function describe(): string
{
// TODO: التفويض لكائنات المكونات وإرجاع سلسلة نصية منسقة
}
}
// قراءة المدخلات
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));
// TODO: إنشاء كائن Computer بالمواصفات المعطاة وطباعة نتيجة describe()
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)