תבנית המרוכבת
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 75 מתוך 87.
תבנית Composite היא תבנית עיצוב מבנית שמאפשרת לך להרכיב אובייקטים למבני עץ כדי לייצג היררכיות של שלם וחלקיו. היא מאפשרת ללקוחות להתייחס באופן אחיד לאובייקטים בודדים ולהרכבים של אובייקטים באמצעות ממשק משותף.
חשבו על מערכת קבצים: תיקייה יכולה להכיל קבצים ותיקיות אחרות, שיכולות להכיל עוד קבצים ותיקיות. בין שאתם עובדים עם קובץ יחיד ובין שאתם עובדים עם מבנה תיקיות שלם, תרצו לבצע פעולות כמו "קבלת גודל" באותו אופן. התבנית מגדירה ממשק Component, אובייקטים מסוג Leaf (רכיבים בודדים) ואובייקטים מסוג Composite (מכולות שמכילות ילדים):
interface FileComponent {
void display(String indent);
int getSize();
}
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}האובייקט המרוכב מעביר את הפעולות לילדים שלו, וכך מאפשר מבנים רקורסיביים. לקוחות מקיימים אינטראקציה עם העץ בלי לדעת אם הם עובדים עם עלה או עם אובייקט מרוכב:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");תבנית ה-Composite אידיאלית לייצוג היררכיות כמו תרשימים ארגוניים, רכיבי ממשק משתמש או מערכות תפריטים, שבהן צריך להתייחס לקבוצות וליחידים באופן זהה.
אתגר
קלבואו נבנה מערכת תרשים ארגוני באמצעות תבנית Composite! תיצרו מבנה שבו מחלקות יכולות להכיל עובדים ותתי-מחלקות אחרות, וכך תוכלו לחשב את סך המשכורות ולהציג את ההיררכיה באופן אחיד, בין אם אתם מסתכלים על עובד יחיד ובין אם על חטיבה שלמה.
תארגנו את הקוד שלכם בארבעה קבצים:
OrganizationComponent.java: הגדירו את ממשק הרכיב שגם עובדים וגם מחלקות יממשו. עליו להצהיר על שתי מתודות:showDetails(String indent)להצגת הרכיב עם הזחה מתאימה, ו-getSalary()שמחזירה את סך המשכורות כמספר שלם.Employee.java: צרו מחלקת עלה שמייצגת עובדים יחידים. ל-Employeeיש שם (String) ומשכורת (int), ושניהם נקבעים באמצעות הבנאי. כאשר קוראים ל-showDetails, עליה להדפיס[indent][name]: $[salary]. המתודהgetSalaryפשוט מחזירה את המשכורת של העובד.Department.java: צרו מחלקה מורכבת שיכולה להכיל עובדים ותתי-מחלקות. ל-Departmentיש שם (String), והיא מנהלת רשימה של ילדים מסוגOrganizationComponent. עליה לכלול מתודהadd(OrganizationComponent component)להוספת חברים. כאשר קוראים ל-showDetails, היא מדפיסה[indent][name] Department, ואז קוראת ל-showDetailsשל כל ילד עם הזחה מוגדלת (הוסיפו שני רווחים). המתודהgetSalaryמחזירה את סכום המשכורות של כל הילדים.Main.java: בנו את הארגון שלכם! תקבלו ארבעה קלטים: שמותיהם של שני עובדים והמשכורות שלהם, וכן שם של תת-מחלקה ועובד אחד.צרו מחלקת "Engineering" בתור השורש. הוסיפו אליה שני עובדים באמצעות שני זוגות השם והמשכורת הראשונים. לאחר מכן צרו תת-מחלקה באמצעות הקלט השלישי, הוסיפו אליה עובד אחד באמצעות זוג השם והמשכורת הרביעי, והוסיפו את תת-המחלקה הזו ל-Engineering.
קראו ל-
showDetails("")במחלקת Engineering, ואז הדפיסו את סך המשכורות בפורמטTotal Salary: $[amount].
תקבלו את הקלטים בסדר הבא: שם העובד employee1 (String), המשכורת של employee1 (int), שם העובד employee2 (String), המשכורת של employee2 (int), שם תת-המחלקה (String), שם העובד employee3 (String), המשכורת של employee3 (int).
לדוגמה, עבור הקלטים Alice, 75000, Bob, 65000, QA, Charlie, 55000, הפלט שלכם יהיה:
Engineering Department
Alice: $75000
Bob: $65000
QA Department
Charlie: $55000
Total Salary: $195000שימו לב שאותן המתודות showDetails ו-getSalary פועלות בצורה חלקה, בין אם קוראים להן עבור עובד יחיד ובין אם עבור מחלקה שלמה שמכילה תתי-מחלקות מקוננות. המבנה המורכב מטפל ברקורסיה באופן אוטומטי, ומאפשר לכם להתייחס לכל ההיררכיה באופן אחיד!
נסו בעצמכם
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// קרא את נתוני הקלט
String emp1Name = scanner.nextLine();
int emp1Salary = Integer.parseInt(scanner.nextLine());
String emp2Name = scanner.nextLine();
int emp2Salary = Integer.parseInt(scanner.nextLine());
String subDeptName = scanner.nextLine();
String emp3Name = scanner.nextLine();
int emp3Salary = Integer.parseInt(scanner.nextLine());
// TODO: צור את המחלקה "Engineering" כמחלקת השורש
// TODO: צור והוסף שני עובדים למחלקת Engineering באמצעות הנתונים של emp1 ו-emp2
// TODO: צור תת-מחלקה באמצעות subDeptName
// TODO: צור והוסף עובד אחד לתת-המחלקה באמצעות הנתונים של emp3
// TODO: הוסף את תת-המחלקה למחלקת Engineering
// TODO: קרא ל-showDetails("") במחלקת Engineering
// TODO: הדפס את השכר הכולל בפורמט: Total Salary: $[amount]
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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ממשקים ומחלקות מופשטות
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםמתודות ברירת מחדל וסטטיות בממשקמחלקות מופשטות לעומת ממשקיםממשקים פונקציונלייםסיכום – מערכת תשלומים9גנריות
מבוא לגנריותמחלקות גנריותשיטות גנריותפרמטרי טיפוס מוגבליםתווים כלליים (?, extends, super)סיכום – מכל גנרי12תבניות עיצוב — חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובתבנית המתודה התבניתיתתבנית המצבתבנית המרוכבתתבנית האיטרטורתרגלו בעצמכם: קומפיילר Java אונליין