הרכבה מול ירושה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 47 מתוך 87.
ירושה יוצרת קשר של "הוא-סוג-של": Dog הוא Animal. אבל מה אם מחלקה זקוקה לפונקציונליות של משהו שהיא אינה? כאן נכנסת לתמונה הרכבה, שיוצרת במקום זאת קשר של "יש-לו".
בקומפוזיציה, מחלקה מכילה מופעים של מחלקות אחרות כשדות במקום לרשת מהן:
// גישת הירושה: Car הוא Engine (לא הגיוני)
class Car extends Engine { }
// גישת ההכלה: ל-Car יש Engine (הגיוני)
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.ignite();
}
}הרכבה מציעה כמה יתרונות. אפשר לשנות את האובייקט המורכב בזמן ריצה, למשל להחליף מנועים.
אפשר גם לשלב התנהגויות ממספר מחלקות, דבר שירושה אינה מאפשרת ב-Java. המחלקה המורכבת חושפת רק את המתודות שהיא בוחרת, וכך מספקת כימוס טוב יותר.
הנחיה מעשית: השתמשו בירושה כשיש קשר אמיתי מסוג „הוא סוג של”, וכשהמחלקה היורשת אכן מייצגת גרסה מיוחדת של מחלקת האב.
השתמשו בהרכבה כשצריך להשתמש בפונקציונליות של מחלקה אחרת בלי להיות מאותו סוג. מפתחים מנוסים רבים פועלים לפי העיקרון: "העדיפו הרכבה על פני ירושה", כי כך מתקבל קוד גמיש יותר וקל יותר לתחזוקה.
אתגר
קלבואו נבנה מערכת מחשב באמצעות הרכבה כדי להדגים את הקשר „יש לו”. במקום ש־Computer יירש את הרכיבים שלו (מה שלא היה הגיוני), המחשב שלך יכיל את החלקים הדרושים לו כדי לפעול.
תארגן את הקוד שלך בשלושה קבצים:
Processor.java: צור מחלקה שמייצגת CPU. למחלקת Processor יש שני שדות פרטיים:brand(String) ו־speedGHz(double). כלול בנאי שמאתחל את שני השדות ומתודות getter עבור כל אחד מהם. הוסף מתודתprocess()שמחזירה String:[brand] processing at [speedGHz] GHzMemory.java: צור מחלקה שמייצגת RAM. למחלקת Memory יש שני שדות פרטיים:type(String, כמו "DDR4" או "DDR5") ו־sizeGB(int). כלול בנאי, מתודות getter ומתודתload()שמחזירה:Loading [sizeGB]GB [type] memoryComputer.java: כאן ההרכבה באה לידי ביטוי! למחלקת Computer שלך צריכים להיות Processor ו־Memory כשדות פרטיים. היא לא מרחיבה אותם, אלא מכילה אותם. כלול בנאי שמקבל את שני הרכיבים. הוסף מתודתboot()שמחזירה String רב־שורות המציג את הפעלת המחשב באמצעות הרכיבים שלו:הוסף גם מתודתBooting computer... [processor.process() result] [memory.load() result] System ready!getSpecs()שמחזירה:Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]Main.java: הפח חיים במערכת שהרכבת! יתקבלו ארבעה קלטים: מותג המעבד (String), מהירות המעבד (double), סוג הזיכרון (String) וגודל הזיכרון (int).צור Processor ו־Memory עם הערכים האלה, ואז הרכב אותם לתוך Computer. הדפס את התוצאה של קריאה ל־
boot(), ואז הדפס את התוצאה שלgetSpecs().
יתקבלו ארבעה קלטים לפי הסדר: מותג המעבד, מהירות המעבד (GHz), סוג הזיכרון וגודל הזיכרון (GB).
שים לב איך Computer מאצילה את העבודה לרכיבים שלה במקום לנסות להיות הם. זהו כוחה של ההרכבה! Computer יכולה להשתמש בכל Processor או Memory שתיתן לה, וכך העיצוב נעשה גמיש ומציאותי.
נסו בעצמכם
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קרא את הקלטים
String processorBrand = scanner.nextLine();
double processorSpeed = Double.parseDouble(scanner.nextLine());
String memoryType = scanner.nextLine();
int memorySize = Integer.parseInt(scanner.nextLine());
// TODO: צור Processor עם המותג והמהירות
// TODO: צור Memory עם הסוג והגודל
// TODO: צור Computer על ידי שילוב של Processor ו-Memory
// TODO: הדפס את התוצאה של boot()
// TODO: הדפס את התוצאה של getSpecs()
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותשדות (מאפיינים)מתודת הבנאיהעמסת בנאיםחזרה – מחשבון פשוט4ירושה
ירושה בסיסית (extends)מילת המפתח superדריסת מתודות (@Override)שרשור בנאיםהמחלקה Objectירושה יחידה ורב־רמתיתלמה אין ירושה מרובה של מחלקותסיכום – היררכיית עובדים7מתודות מיוחדות ומחלקת Object
מתודת toString()equals() ו-hashCode()מתודת clone()compareTo() ו-Comparableהממשק Comparatorסיכום – מיון מותאם אישית2מצייני גישה ואנקפסולציה
סקירה של רמות הגישהמתודות Getter ו-Setterהסתרת מידעמילת המפתח finalסיכום – מנהל חשבון בנק5פולימורפיזם
יסודות העמסת מתודותדריסת מתודות (בזמן ריצה)המרה כלפי מעלה וכלפי מטההאופרטור instanceofמחלקות ומתודות מופשטותסיכום – מחשבון צורות8מושגי OOP מתקדמים
הרכבה מול ירושהאגרגציה מול הרכבהמחלקות פנימיות, מקוננות ואנונימיותEnums ומתודות של EnumRecords (Java 16+)מחלקות חתומות (Java 17+)3מאפייני מחלקה וחברים סטטיים
משתני מופע לעומת משתנים סטטייםמתודות סטטיותבלוקים סטטייםקבועים (static final)סיכום — מונה ומחלקת עזר6ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומיםתרגלו בעצמכם: קומפיילר Java אונליין