אגרגציה מול הרכבה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 48 מתוך 87.
גם אגרגציה וגם קומפוזיציה הן צורות של קשרי „יש-לו”, אך הן נבדלות בהיבט מכריע אחד: בעלות ותלות במחזור החיים.
בהרכבה, האובייקט המוכל אינו יכול להתקיים באופן עצמאי מהמכיל. כשהמכיל מושמד, גם החלקים שלו מושמדים. חשבו על House ועל אובייקטי Room שלו: חדרים אינם קיימים בלי הבית:
class House {
private Room[] rooms;
public House(int numRooms) {
rooms = new Room[numRooms];
for (int i = 0; i < numRooms; i++) {
rooms[i] = new Room(); // House יוצרת את החדרים והם בבעלותה
}
}
}בצבירה, האובייקט הכלול יכול להתקיים באופן עצמאי. המכולה משתמשת באובייקט אך אינה שולטת במחזור החיים שלו. חשבו על Team ועל אובייקטי Player שלו: שחקנים קיימים גם ללא קבוצה:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team משתמשת בשחקנים קיימים
}
}ההבדל העיקרי: בהרכבה, המכל יוצר את החלקים שלו באופן פנימי. בצבירה, אובייקטים מועברים פנימה מבחוץ. כאשר Team נמחקת, האובייקטים מסוג Player עדיין קיימים. כאשר House נמחקת, גם האובייקטים מסוג Room שלה נעלמים.
אתגר
קלבואו נבנה מערכת קורסים אוניברסיטאית המדגימה גם צבירה וגם הרכבה. תראו איך Course מכילה את אובייקטי ה־Lecture שלה (הרכבה), בעוד שהיא רק מפנה לאובייקטי Professor שקיימים באופן עצמאי (צבירה).
תארגנו את הקוד שלכם בארבעה קבצים:
Professor.java: צרו מחלקה המייצגת פרופסור שיכול להתקיים באופן עצמאי, ללא תלות בקורס כלשהו. למחלקה Professor יש שני שדות פרטיים:name(String) ו־department(String). כללו בנאי לאתחול שני השדות ומתודות getter עבור כל אחד מהם. דרסו אתtoString()כך שתחזיר:Prof. [name] ([department])Lecture.java: צרו מחלקה המייצגת הרצאה שיש לה משמעות רק במסגרת קורס. למחלקה Lecture יש שני שדות פרטיים:topic(String) ו־durationMinutes(int). כללו בנאי ומתודות getter. דרסו אתtoString()כך שתחזיר:Lecture: [topic] ([durationMinutes] min)Course.java: כאן תדגימו את שני סוגי הקשרים! למחלקה Course יש שלושה שדות פרטיים:title(String),professor(Professor - צבירה), ו־lectures(מערך של אובייקטי Lecture - הרכבה).הבנאי מקבל את שם הקורס, אובייקט Professor (שמועבר מבחוץ — צבירה) ואת מספר ההרצאות. בתוך הבנאי, צרו את מערך ה־Lecture ומלאו אותו באובייקטי Lecture חדשים. עבור כל הרצאה באינדקס
i, צרו אותה עם הנושא"Topic " + (i + 1)ועם משך של45דקות. יצירה פנימית זו מדגימה הרכבה.הוסיפו מתודת
getInfo()שמחזירה String בן כמה שורות:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]הוסיפו גם מתודת
listLectures()שמחזירה String שבו ה־toString() של כל הרצאה מופיע בשורה נפרדת.Main.java: חברו את כל החלקים! תקבלו ארבעה קלטים: שם הפרופסור (String), המחלקה של הפרופסור (String), שם הקורס (String) ומספר ההרצאות (int).תחילה, צרו Professor עם השם והמחלקה שסופקו. הפרופסור הזה קיים באופן עצמאי. לאחר מכן, צרו Course והעבירו אליו את שם הקורס, את הפרופסור (צבירה) ואת מספר ההרצאות (הקורס ייצור את ההרצאות שלו באופן פנימי — הרכבה).
הדפיסו את התוצאה של
getInfo(), לאחר מכן הדפיסו שורה ריקה, ואז הדפיסו את התוצאה שלlistLectures().
תקבלו ארבעה קלטים לפי הסדר: שם הפרופסור, המחלקה, שם הקורס ומספר ההרצאות.
שימו לב להבדל המרכזי: ה־Professor נוצר מחוץ ל־Course ומועבר אליו (צבירה: הפרופסור יכול ללמד קורסים אחרים או להתקיים גם בלי הקורס הזה). ה־Lectures נוצרות בתוך הבנאי של Course (הרכבה). הן שייכות בלעדית לקורס הזה ולא היו קיימות בלעדיו.
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קראו את הקלטים
String professorName = scanner.nextLine();
String department = scanner.nextLine();
String courseTitle = scanner.nextLine();
int numberOfLectures = scanner.nextInt();
// TODO: צרו אובייקט Professor (קיים באופן עצמאי - אגרגציה)
// TODO: צרו אובייקט Course והעבירו אליו את המרצה (אגרגציה)
// הקורס ייצור את ההרצאות שלו בעצמו באופן פנימי (קומפוזיציה)
// TODO: הדפיסו את התוצאה של getInfo()
// TODO: הדפיסו שורה ריקה
// TODO: הדפיסו את התוצאה של listLectures()
}
}השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין