הטמעת כמה Structs
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 37 מתוך 107.
Go מאפשרת לך לשלב כמה מבנים בתוך מבנה יחיד, וכך לשלב התנהגויות ממקורות שונים. הדבר דומה לירושה מרובה בשפות אחרות, אך משתמש בגישת ההרכבה של Go.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
למבנה Service יש עכשיו גישה למתודות של שני הטיפוסים המוטמעים:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order
}
בעת שילוב של כמה מבנים, עלול להיווצר קונפליקט בשמות אם לשני טיפוסים משולבים יש מתודות בעלות אותו שם. Go לא פותרת את העמימות הזאת באופן אוטומטי:
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - שגיאת קומפילציה: בורר דו־משמעי
fmt.Println(c.A.Greet()) // שלום מ־A
fmt.Println(c.B.Greet()) // שלום מ־B
}
כאשר מתעוררות התנגשויות, עליך לציין במפורש לאיזו מתודה של טיפוס מוטמע ברצונך לקרוא, באמצעות שם הטיפוס כמסייג.
אתגר
קלבואו נבנה מערכת של מכשירי בית חכם המשלבת כמה יכולות באמצעות הטמעת struct. תיצרו מכשיר שיוכל גם לשלוט בתאורה וגם להשמיע מוזיקה, באמצעות הטמעת טיפוסי בקר נפרדים.
תארגנו את הקוד בשלושה קבצים:
controllers.go: צרו שתי structs עצמאיות של בקרים, המספקות יכולות שונות:LightControllerעם שדהBrightnessמסוג (int) ומתודהSetLight(level int) stringשמחזירהLight set to [level]%AudioControllerעם שדהVolumeמסוג (int) ומתודהSetVolume(level int) stringשמחזירהVolume set to [level]%
Status() string. כך נוצר מצב של התנגשות שמות שתצטרכו לטפל בו.LightController.Status()צריכה להחזירBrightness: [Brightness]%, ו-AudioController.Status()צריכה להחזירVolume: [Volume]%.device.go: צרו struct בשםSmartDeviceעם שדהName, המטמיעה גם אתLightControllerוגם אתAudioController. הוסיפו מתודה בשםFullStatus() stringשמחזירה את שם המכשיר יחד עם הסטטוסים של שני הבקרים, ופותרת את העמימות באמצעות קריאה מפורשת למתודתStatus()של כל טיפוס מוטמע.main.go: קראו את תצורת המכשיר מהקלט, צרוSmartDevice, השתמשו במתודות המקודמות כדי להגדיר את רמות התאורה ועוצמת הקול, ואז הציגו את הסטטוס המלא.
הקלט הבא יסופק:
- שורה 1: שם המכשיר
- שורה 2: רמת הבהירות ההתחלתית (מספר שלם)
- שורה 3: עוצמת הקול ההתחלתית (מספר שלם)
- שורה 4: רמת הבהירות החדשה להגדרה (מספר שלם)
- שורה 5: עוצמת הקול החדשה להגדרה (מספר שלם)
המתודה FullStatus() שלכם צריכה להחזיר:
[Name] - [LightController.Status()], [AudioController.Status()]הדפיסו את התוצאות של הקריאות ל-SetLight ול-SetVolume (המקודמות מהטיפוסים המוטמעים), ואז הדפיסו את הסטטוס המלא.
לדוגמה, עבור Living Room Hub, 50, 30, 75 ו-60, הפלט שלכם צריך להיות:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%שימו לב שאפשר לגשת ישירות ל-SetLight ול-SetVolume דרך SmartDevice באמצעות קידום מתודות, אבל Status() דורשת ציון מפורש של הטיפוס, משום שלשני הטיפוסים המוטמעים יש מתודה בשם זה.
נסו בעצמכם
package main
import (
"fmt"
)
func main() {
// קרא את הקלט
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: צור SmartDevice עם השם והערכים ההתחלתיים הנתונים
// TODO: השתמש במתודה SetLight שקודמה והדפס את התוצאה
// TODO: השתמש במתודה SetVolume שקודמה והדפס את התוצאה
// TODO: הדפס את המצב המלא באמצעות המתודה FullStatus()
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין