תבנית המורכב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 96 מתוך 107.
תבנית הComposite מאפשרת להתייחס לאובייקטים יחידים ולקבוצות של אובייקטים באופן אחיד. בעוד ש-State משנה את ההתנהגות על סמך מצב פנימי, Composite בונה מבני עץ שבהם גם עלים וגם מכלים חולקים את אותו ממשק.
התבנית הזאת מתאימה מאוד למבנים היררכיים כמו מערכות קבצים, תרשימי ארגון או רכיבי ממשק משתמש.
type Component interface {
GetSize() int
}
type File struct {
Name string
Size int
}
func (f File) GetSize() int {
return f.Size
}המורכב (המכיל) מחזיק צאצאים ומממש את אותו ממשק באמצעות צבירת התוצאות שלהם:
type Folder struct {
Name string
Children []Component
}
func (f *Folder) Add(c Component) {
f.Children = append(f.Children, c)
}
func (f Folder) GetSize() int {
total := 0
for _, child := range f.Children {
total += child.GetSize()
}
return total
}עכשיו אפשר לקנן תיקיות בתוך תיקיות, והקריאה ל־GetSize() פועלת באותו אופן, בין אם מדובר בקובץ יחיד ובין אם בעץ תיקיות שלם:
docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})
root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})
fmt.Println(root.GetSize()) // 175דפוס Composite אידיאלי כשצריך לייצג היררכיות של שלם וחלקיו ורוצים שהלקוחות יתייחסו לאובייקטים בודדים ולהרכבות באופן זהה.
אתגר
קלבואו נבנה מערכת לתרשים ארגוני באמצעות תבנית Composite! תיצרו מבנה שבו אפשר להתייחס באופן אחיד גם לעובדים יחידים וגם למחלקות (המכילות עובדים או מחלקות אחרות), דבר שמתאים במיוחד לחישוב סך המשכורות בכל חלק של הארגון.
תארגנו את הקוד בשלושה קבצים:
component.go: הגדירו את הממשק המשותף שגם יחידים וגם קבוצות יממשו.צרו ממשק
OrgComponentעם שתי מתודות:GetSalary() int: מחזירה את סך המשכורת של הרכיב הזהGetName() string: מחזירה את שם העובד או המחלקה
organization.go: ממשו את הטיפוסים של העלה (עובד יחיד) ושל המורכב (מחלקה).צרו מבנה
Employeeעם השדותName(מחרוזת) ו-Salary(מספר שלם). המתודהGetSalary()שלו מחזירה את המשכורת שלו, והמתודהGetName()מחזירה את שמו.צרו מבנה
Departmentעם שדהName(מחרוזת) ופרוסתMembersשמכילה פריטים מסוגOrgComponent. הוסיפו מתודהAdd(c OrgComponent)כדי להוסיף חברים. המתודהGetSalary()שלו צריכה להחזיר את סכום המשכורות של כל החברים, והמתודהGetName()מחזירה את שם המחלקה.main.go: בנו מבנה ארגוני וחשבו משכורות.קראו את מספר העובדים. עבור כל עובד, קראו את שמו ואת משכורתו וצרו מופעי
Employee. לאחר מכן קראו את מספר המחלקות. עבור כל מחלקה, קראו את שמה ואת מספר האינדקסים של החברים, ולאחר מכן את האינדקסים האלה (ממוספרים החל מ-0, ומתייחסים לעובדים או למחלקות שנוצרו קודם לכן לפי סדר יצירתם).לאחר בניית המבנה, קראו אינדקס סופי והדפיסו את שם הרכיב הזה ואת סך המשכורת שלו בפורמט:
[name]: [salary]
הקלטים הבאים יסופקו:
- מספר העובדים, ולאחר מכן השם והמשכורת של כל עובד בשורות נפרדות
- מספר המחלקות, ולאחר מכן שם כל מחלקה, מספר החברים והאינדקסים שלהם
- האינדקס הסופי לבדיקה
לדוגמה, עבור הקלט:
3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3הפלט שלכם צריך להיות:
Engineering: 165000ועבור הקלט:
4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4הפלט שלכם צריך להיות:
QA: 125000ועבור הקלט:
2
Alice
50000
Bob
60000
0
1הפלט שלכם צריך להיות:
Bob: 60000שימו לב ש-GetSalary() פועלת באופן זהה בין אם קוראים לה על עובד יחיד ובין אם על מחלקה שלמה: תבנית Composite מאפשרת להתייחס ליחידים ולקבוצות באופן אחיד באמצעות אותו ממשק!
נסו בעצמכם
package main
import "fmt"
func main() {
// קרא את מספר העובדים
var numEmployees int
fmt.Scanln(&numEmployees)
// אחסן את כל הרכיבים (עובדים ומחלקות) לפי סדר יצירתם
var components []OrgComponent
// TODO: קרא את השם והשכר של כל עובד
// צור מופעים של Employee והוסף אותם לפרוסה components
for i := 0; i < numEmployees; i++ {
var name string
var salary int
fmt.Scanln(&name)
fmt.Scanln(&salary)
// TODO: צור עובד והוסף אותו ל-components
}
// קרא את מספר המחלקות
var numDepartments int
fmt.Scanln(&numDepartments)
// TODO: קרא את השם, מספר החברים והאינדקסים של החברים בכל מחלקה
// צור מופעים של Department, הוסף חברים לפי אינדקס והוסף אותם ל-components
for i := 0; i < numDepartments; i++ {
var deptName string
var memberCount int
fmt.Scanln(&deptName)
fmt.Scanln(&memberCount)
// TODO: צור מחלקה, קרא את האינדקסים של החברים, הוסף חברים והוסף אותה ל-components
}
// קרא את האינדקס האחרון לשאילתה
var queryIndex int
fmt.Scanln(&queryIndex)
// TODO: הדפס את שם הרכיב ואת השכר הכולל בפורמט: [name]: [salary]
// דוגמה: fmt.Printf("%s: %d\n", name, salary)
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API14תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובתבנית שיטת התבניתתבנית המצבתבנית המורכבMiddleware בתור Decorator3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין