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

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

جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 76 من 104.

عند تصميم العلاقات بين الأصناف (classes)، لديك خياران رئيسيان: الوراثة ("is-a") و التركيب ("has-a"). يؤثر اختيار النهج الصحيح بشكل كبير على مرونة الكود وقابليته للصيانة.

تنشئ الوراثة ارتباطاً وثيقاً بين الفئات. فالفئة Car التي ترث من Vehicle مرتبطة بشكل دائم بتلك العلاقة:

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

التركيب (Composition) يُدرج الكائنات كأعضاء، مما يخلق علاقة أكثر مرونة. الـ Car يمتلك Engine بدلاً من أن يكون محركاً:

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // السيارة لديها محرك (Car HAS an Engine)
public:
    void start() { engine.start(); }
};

الميزة الأساسية للتركيب (composition) هي المرونة. يمكنك بسهولة تبديل المكونات، أو تغيير السلوك أثناء وقت التشغيل (runtime)، أو دمج قدرات متعددة دون قيود التسلسل الهرمي الصارم للفئات (class hierarchy). يفضل تصميم ++C الحديث التركيب على الوراثة (inheritance) في معظم الحالات.

استخدم الوراثة عندما تكون هناك علاقة "is-a" حقيقية وتحتاج إلى تعدد الأشكال (polymorphism). استخدم التركيب عندما تريد إعادة استخدام الوظائف أو عندما يتم وصف العلاقة بشكل أفضل على أنها "has-a" أو "uses-a".

challenge icon

التحدي

سهل

لنقم ببناء محاكي لنظام كمبيوتر يوضح متى يجب استخدام التركيب (composition) مقابل الوراثة (inheritance). ستقوم بنمذجة جهاز كمبيوتر يحتوي على مكونات (التركيب) بدلاً من أن يكون مكوناً، مما يوضح كيف يوفر التركيب المرونة لتبديل الأجزاء في وقت التشغيل.

ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:

  • Components.h: تحديد المكونات الفردية التي يمكن أن يحتوي عليها الكمبيوتر.

    قم بإنشاء فئة CPU مع سمات خاصة std::string model و int cores. قم بتوفير منشئ (constructor) يأخذ كلتا القيمتين، وطريقة process() تطبع CPU [model] processing with [cores] cores، وطريقة getModel().

    قم بإنشاء فئة Memory مع سمة خاصة int sizeGB. قم بتوفير منشئ، وطريقة load() تطبع Loading data into [sizeGB]GB RAM، وطريقة getSize().

    قم بإنشاء فئة Storage مع سمات خاصة std::string type (مثل "SSD" أو "HDD") و int capacityGB. قم بتوفير منشئ، وطريقة read() تطبع Reading from [capacityGB]GB [type]، وطرق للحصول على (getter methods) لكلا الحقلين.

  • Computer.h: تحديد فئة Computer التي تستخدم التركيب لدمج المكونات.

    يجب أن يحتوي Computer الخاص بك على كائنات أعضاء خاصة: CPU، و Memory، و Storage. استخدم قائمة تهيئة المنشئ (constructor initialization list) لتهيئة هذه المكونات من المعلمات.

    قم بتنفيذ هذه الطرق:

    • boot() — تطبع Booting computer...، ثم تستدعي load() على الذاكرة، و read() على التخزين، و process() على المعالج، كل منها في سطر منفصل.
    • specs() — تطبع مواصفات الكمبيوتر بهذا التنسيق:
      Computer Specs:
      - CPU: [model] ([cores] cores)
      - RAM: [size]GB
      - Storage: [capacity]GB [type]
  • main.cpp: قراءة ستة مدخلات (كل منها في سطر منفصل):
    1. اسم طراز المعالج (string)
    2. عدد نوى المعالج (integer)
    3. حجم الذاكرة بالجيجابايت (integer)
    4. نوع التخزين (string، مثل "SSD")
    5. سعة التخزين بالجيجابايت (integer)
    6. أمر: إما "boot" أو "specs"

    قم بإنشاء كائنات المكونات، ثم قم بإنشاء Computer باستخدام التركيب. بناءً على الأمر، قم باستدعاء إما boot() أو specs().

على سبيل المثال، مع المدخلات Intel i7، و 8، و 16، و SSD، و 512، و boot:

Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores

مع المدخلات AMD Ryzen، و 6، و 32، و HDD، و 1000، و specs:

Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDD

لاحظ كيف أن فئة Computer لا ترث من أي مكون — بل تحتوي عليها. تعني علاقة "has-a" هذه أنه يمكنك بسهولة إنشاء أجهزة كمبيوتر بمجموعات مكونات مختلفة، أو حتى تبديل المكونات لاحقاً إذا أضفت طرق تعيين (setter methods). هذه المرونة هي الميزة الرئيسية للتركيب على الوراثة.

ورقة مرجعية

عند تصميم العلاقات بين الأصناف (classes)، اختر بين الوراثة ("is-a") والتركيب ("has-a").

الوراثة تنشئ ترابطاً وثيقاً بين الأصناف:

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

التركيب يدمج الكائنات كأعضاء، مما يخلق علاقات مرنة:

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // السيارة تمتلك محركاً (Car HAS an Engine)
public:
    void start() { engine.start(); }
};

متى تستخدم كلاً منهما:

  • استخدم الوراثة لعلاقات "is-a" الحقيقية عندما تحتاج إلى تعدد الأشكال (polymorphism)
  • استخدم التركيب لإعادة استخدام الوظائف أو لعلاقات "has-a"/"uses-a"
  • يفضل تصميم C++ الحديث التركيب لمرونته—يمكنك تبديل المكونات، وتغيير السلوك في وقت التشغيل، ودمج القدرات دون تسلسلات هرمية جامدة

جرّب بنفسك

#include <iostream>
#include <string>
#include "Computer.h"

using namespace std;

int main() {
    // قراءة المدخلات
    string cpuModel;
    getline(cin, cpuModel);  // اسم طراز الـ CPU (قد يحتوي على مسافات)
    
    int cpuCores;
    cin >> cpuCores;  // عدد أنوية الـ CPU
    
    int memorySize;
    cin >> memorySize;  // حجم الذاكرة بالـ GB
    
    string storageType;
    cin >> storageType;  // نوع التخزين (مثل "SSD")
    
    int storageCapacity;
    cin >> storageCapacity;  // سعة التخزين بالـ GB
    
    string command;
    cin >> command;  // الأمر: "boot" أو "specs"

    // TODO: إنشاء كائنات المكونات (CPU، Memory، Storage)
    
    // TODO: إنشاء كائن Computer باستخدام الـ composition
    
    // TODO: بناءً على الأمر، استدعِ إما boot() أو specs()

    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming