הרכבת ממשקים
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 31 מתוך 107.
Go מאפשרת לך לבנות ממשקים גדולים יותר באמצעות הרכבה של ממשקים קטנים יותר. במקום להגדיר ממשק גדול אחד עם שיטות רבות, אפשר לשלב ממשקים קיימים בתוך הגדרה של ממשק חדש.
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
// ReadWriter משלב את שני הממשקים
type ReadWriter interface {
Reader
Writer
}הממשק ReadWriter דורש כעת גם את המתודות Read() וגם Write(). כל טיפוס שמממש את שתי המתודות מקיים את ReadWriter, וכן את Reader ואת Writer בנפרד.
type File struct {
Name string
}
func (f File) Read() string { return "file content" }
func (f File) Write(data string) { fmt.Println("Writing:", data) }
func Process(rw ReadWriter) {
content := rw.Read()
rw.Write(content)
}
func main() {
f := File{Name: "data.txt"}
Process(f) // File מממש את ReadWriter
}גישת ההרכבה הזו שומרת על ממשקים קטנים וממוקדים. אפשר לשלב ביניהם לפי הצורך וליצור חוזים מדויקים עבור הפונקציות שלך. הספרייה הסטנדרטית של Go משתמשת בתבנית הזו בהרחבה, למשל io.ReadWriter, שמשלב בין io.Reader ו־io.Writer.
אתגר
קלנבנה מערכת נגן מדיה שמדגימה הרכבה של ממשקים. תיצור ממשקים קטנים וממוקדים, ואז תשלב אותם בממשק מורכב ועוצמתי יותר שטיפוס יחיד יכול לממש.
תארגן את הקוד שלך בשני קבצים:
media.go: הגדר שלושה ממשקים ומבנה שמממש את כולם:- ממשק
Playerעם מתודהPlay() string - ממשק
Recorderעם מתודהRecord(content string) string - ממשק
MediaDeviceשמורכב גם מ־Playerוגם מ־Recorder - מבנה
SmartDeviceעם שדהName, שמממש את כל המתודות הנדרשות
- ממשק
main.go: צור פונקציות שמדגימות כיצד ניתן להשתמש באותוSmartDeviceדרך טיפוסי ממשקים שונים. כתוב שלוש פונקציות:UsePlayerשמקבלתPlayerוקוראת למתודהPlayשלוUseRecorderשמקבלתRecorderומחרוזת תוכן, ואז קוראת ל־RecordUseMediaDeviceשמקבלתMediaDeviceומחרוזת תוכן, ואז קוראת גם ל־Recordוגם ל־Play
SmartDevice, והדגם שימוש בו דרך כל שלושת טיפוסי הממשקים.
הקלטים הבאים יסופקו:
- שורה 1: שם המכשיר
- שורה 2: תוכן להקלטה
המתודות שלך צריכות להחזיר מחרוזות בפורמטים הבאים:
Play():[Name] is playingRecord(content):[Name] recorded: [content]
לדוגמה, עבור MyPhone ו־voice memo, הפלט שלך צריך להיות:
MyPhone is playing
MyPhone recorded: voice memo
MyPhone recorded: voice memo
MyPhone is playingשתי השורות הראשונות מציגות את המכשיר בשימוש דרך הממשקים הנפרדים (Player ו־Recorder). שתי השורות האחרונות מציגות את השימוש בו דרך ממשק MediaDevice המורכב, שדורש את שתי היכולות. שים לב שאותו SmartDevice שלך מממש את כל שלושת הממשקים, משום שיש לו את שתי המתודות הנדרשות.
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: ממשו את UsePlayer שמקבלת Player וקוראת למתודה Play שלו
// עליה להדפיס את התוצאה של Play()
// TODO: ממשו את UseRecorder שמקבלת Recorder ומחרוזת content
// עליה להדפיס את התוצאה של Record(content)
// TODO: ממשו את UseMediaDevice שמקבלת MediaDevice ומחרוזת content
// עליה להדפיס את התוצאה של Record(content) ולאחר מכן של Play()
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
deviceName := scanner.Text()
scanner.Scan()
content := scanner.Text()
// TODO: צרו SmartDevice עם השם הנתון
// TODO: השתמשו בהתקן דרך הממשק Player
// TODO: השתמשו בהתקן דרך הממשק Recorder
// TODO: השתמשו בהתקן דרך הממשק MediaDevice
_ = deviceName
_ = content
fmt.Println("Implement the solution")
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין