Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

التركيب مقابل الوراثة

جزء من قسم 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) من خلال التحكم في كيفية تلقي الكائنات لتبعياتها.

challenge icon

التحدي

سهل

لنقم ببناء نظام كمبيوتر باستخدام التركيب (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()

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming