Menu
Coddy logo textTech

הרכבה לעומת הורשה

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 76 מתוך 104.

כשמתכננים קשרים בין מחלקות, יש שתי אפשרויות עיקריות: ירושה ("הוא-") והרכבה ("יש לו"). הבחירה בגישה הנכונה משפיעה במידה רבה על הגמישות ועל יכולת התחזוקה של הקוד שלך.

ירושה יוצרת צימוד הדוק בין מחלקות. 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"; }
};

הרכבה משלבת אובייקטים כחברים, וכך יוצרת קשר גמיש יותר. ל-Car יש Engine במקום להיות מנוע:

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

class Car {
private:
    Engine engine;  // ל-Car יש Engine
public:
    void start() { engine.start(); }
};

היתרון המרכזי של הרכבה הוא הגמישות. אפשר להחליף רכיבים בקלות, לשנות התנהגות בזמן ריצה או לשלב כמה יכולות, בלי המגבלות של היררכיית מחלקות קשיחה. ברוב המקרים, עיצוב מודרני ב-C++ מעדיף הרכבה על פני ירושה.

השתמשו בירושה כשיש קשר אמיתי מסוג "הוא-סוג-של" ונחוצה לכם פולימורפיות. השתמשו בהרכבה כשרוצים לעשות שימוש חוזר בפונקציונליות, או כשהקשר מתואר טוב יותר כ"יש-לו" או "משתמש-ב".

challenge icon

אתגר

קל

בואו נבנה סימולטור של מערכת מחשב שמדגים מתי להשתמש בהרכבה לעומת בירושה. תדגמנו מחשב שיש לו רכיבים (הרכבה), במקום להיות רכיב, וכך נראה כיצד הרכבה מאפשרת להחליף חלקים בזמן ריצה.

תארגנו את הקוד בשלושה קבצים:

  • Components.h: הגדירו את הרכיבים הנפרדים שמחשב יכול להכיל.

    צרו מחלקת CPU עם std::string model ו-int cores פרטיים. ספקו בנאי שמקבל את שני הערכים, מתודת 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], ומתודות לקבלת הערכים של שני השדות.

  • Computer.h: הגדירו מחלקת Computer שמשתמשת בהרכבה כדי לשלב רכיבים.

    למחלקת Computer צריכים להיות אובייקטים פרטיים של משתני חבר: CPU,‏ Memory ו-Storage. השתמשו ברשימת האתחול של הבנאי כדי לאתחל את הרכיבים האלה מתוך הפרמטרים.

    ממשו את המתודות הבאות:

    • boot(): מדפיסה Booting computer..., ואז קוראת ל-load() על הזיכרון, ל-read() על האחסון ול-process() על המעבד, כל אחת בשורה נפרדת
    • specs(): מדפיסה את מפרט המחשב בפורמט הבא:
      Computer Specs:
      - CPU: [model] ([cores] cores)
      - RAM: [size]GB
      - Storage: [capacity]GB [type]
  • main.cpp: קראו שש קלטים (כל אחד בשורה נפרדת):
    1. שם דגם המעבד (מחרוזת)
    2. מספר ליבות המעבד (מספר שלם)
    3. גודל הזיכרון ב-GB (מספר שלם)
    4. סוג האחסון (מחרוזת, למשל "SSD")
    5. קיבולת האחסון ב-GB (מספר שלם)
    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 אינה יורשת מאף רכיב. היא מכילה אותם. קשר מסוג "יש-לו" פירושו שתוכלו ליצור בקלות מחשבים עם שילובים שונים של רכיבים, או אפילו להחליף רכיבים בהמשך אם תוסיפו מתודות setter. הגמישות הזאת היא היתרון המרכזי של הרכבה על פני ירושה.

נסו בעצמכם

#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 באמצעות קומפוזיציה
    
    // TODO: בהתאם לפקודה, לקרוא ל-boot() או ל-specs()

    return 0;
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C++ אונליין