אוספים פולימורפיים
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 44 מתוך 107.
אחד השימושים העוצמתיים ביותר בממשקים הוא אחסון של טיפוסים שונים באותו אוסף. פרוסה מטיפוס של ממשק יכולה להכיל כל ערך שמקיים את הממשק הזה, וכך לאפשר לך לקבץ יחד טיפוסים קשורים אך שונים.
נניח שעליך לנהל צורות שונות. במקום פרוסות נפרדות לכל סוג, אפשר להשתמש בפרוסה אחת מסוג הממשק:
type Shape interface {
Area() float64
}
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 3},
Circle{Radius: 2},
}
for _, s := range shapes {
fmt.Printf("Area: %.2f\n", s.Area())
}
}
הפרוסה shapes מכילה גם עיגולים וגם מלבנים. בעת איטרציה, כל איבר מגיב ל־Area() בהתאם לטיפוס שלו בפועל. זהו פולימורפיזם המיושם על אוספים: אותה לולאה מטפלת בכל סוגי הצורות באופן אחיד.
אוספים פולימורפיים חיוניים לבניית מערכות המעבדות פריטים מסוגים שונים: מערכת התראות ששולחת הודעות דוא״ל והודעות SMS, משחק שמעדכן סוגים שונים של אויבים, או מעבד מסמכים המטפל בפורמטים מרובים של קבצים. לאוסף לא אכפת מהסוגים הספציפיים: רק שכל רכיב יספק את ההתנהגות הנדרשת.
אתגר
קלבואו נבנה מערכת לניהול משימות שמטפלת בסוגים שונים של משימות באמצעות ממשק אחיד. תיצרו סוגי משימות שונים ותעבדו עם כולם יחד באוסף פולימורפי.
תארגנו את הקוד בשלושה קבצים:
task.go: הגדירו ממשקTaskשדורש מתודהSummary() string. הממשק הזה יהיה החוזה המשותף שכל סוגי המשימות צריכים לממש.types.go: צרו שלושה סוגי משימות שונים, שכל אחד מהם מממש את הממשקTask:BugFixעם השדותID(מחרוזת) ו-Severity(מחרוזת). המתודהSummary()מחזירהBug #[ID] ([Severity])Featureעם השדותName(מחרוזת) ו-Points(מספר שלם). המתודהSummary()מחזירהFeature: [Name] - [Points] ptsDocumentationעם השדהTopic(מחרוזת). המתודהSummary()מחזירהDocs: [Topic]
main.go: צרו פונקציה בשםPrintBacklogשמקבלת פרוסה שלTaskומדפיסה את הסיכום של כל משימה בשורה משלה. קראו את פרטי המשימות מהקלט, צרו משימה אחת מכל סוג, אספו את כולן לפרוסת[]Taskאחת והעבירו אותה אלPrintBacklog.
הקלט שיסופק יהיה בפורמט הבא:
- שורה 1: מזהה הבאג
- שורה 2: חומרת הבאג
- שורה 3: שם התכונה
- שורה 4: נקודות התכונה (מספר שלם)
- שורה 5: נושא התיעוד
לדוגמה, עבור 1042, critical, Dark Mode, 8 ו-API Reference, הפלט יהיה:
Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Referenceהיתרון כאן הוא ש-PrintBacklog לא צריכה לוגיקה נפרדת עבור באגים, תכונות או תיעוד. היא פשוט עוברת על הפרוסה וקוראת ל-Summary() של כל איבר. כל סוג משימה מגיב בפורמט הייחודי שלו, וכך מודגם כיצד אוספים פולימורפיים מאפשרים לעבד סוגים מגוונים באופן אחיד.
נסו בעצמכם
package main
import (
"fmt"
)
// TODO: צרו פונקציה בשם PrintBacklog שמקבלת פרוסה של Task
// ומדפיסה את הסיכום של כל משימה בשורה נפרדת
func main() {
// קראו קלט
var bugID string
var bugSeverity string
var featureName string
var featurePoints int
var docTopic string
fmt.Scanln(&bugID)
fmt.Scanln(&bugSeverity)
fmt.Scanln(&featureName)
fmt.Scanln(&featurePoints)
fmt.Scanln(&docTopic)
// TODO: צרו מופע אחד מכל סוג משימה (BugFix, Feature, Documentation)
// TODO: אספו את כל המשימות לפרוסת []Task אחת
// TODO: קראו ל־PrintBacklog עם פרוסת המשימות
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין