Menu
Coddy logo textTech

מימוש משתמע

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 26 מתוך 107.

בשפות מונחות עצמים רבות כמו Java או C#, צריך להצהיר במפורש שסוג מממש ממשק באמצעות מילות מפתח כמו implements. Go נוקטת גישה אחרת: מימוש ממשק הוא מרומז.

טיפוס עומד בדרישות של ממשק פשוט על ידי מימוש כל המתודות שלו. אין צורך להצהיר על הקשר בשום מקום. המהדר מסיק זאת באופן אוטומטי.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter מממש את Writer באופן מרומז
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

שימו לב שאין הצהרת implements Writer ב-FileWriter. מכיוון של-FileWriter יש מתודה Write(data string) int שתואמת לחתימת הממשק, הוא מקיים באופן אוטומטי את הממשק Writer.

לעיצוב הזה יש תוצאה עוצמתית: אפשר להגדיר ממשקים אחרי שסוגים כבר קיימים. אם לספרייה של צד שלישי יש סוג עם מתודות שנחוצות לך, אפשר ליצור ממשק שהסוג הזה כבר עומד בדרישות שלו, בלי לשנות את הקוד המקורי.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // עובד כי FileWriter מממש את Writer
}

מימוש משתמע שומר על קוד Go מנותק וגמיש. טיפוסים לא צריכים להכיר ממשקים מראש, וכך קל יותר להרכיב מערכות מחלקים עצמאיים.

challenge icon

אתגר

קל

בואו נבנה מערכת להעברת הודעות שמדגימה כיצד טיפוסים מממשים ממשקים באופן מרומז ב-Go. תיצרו טיפוסי שליחים שונים שכולם תואמים לאותו ממשק, בלי להצהיר במפורש על הקשר הזה.

תארגנו את הקוד שלכם בשני קבצים:

  • messengers.go: הגדירו ממשק Messenger עם מתודה יחידה Send(message string) string. לאחר מכן צרו שתי מבנים שיתאימו לממשק הזה באופן מרומז:
    • EmailMessenger עם שדה Address (מחרוזת)
    • SMSMessenger עם שדה PhoneNumber (מחרוזת)
    לכל מבנה צריכה להיות מתודה Send שמחזירה מחרוזת המתארת את המסירה. אף אחד מהמבנים לא צריך להצהיר במפורש שהוא מממש את Messenger: Go יזהה זאת אוטומטית על סמך חתימות המתודות.
  • main.go: צרו פונקציה בשם DeliverMessage שמקבלת כל Messenger ומחרוזת הודעה, ולאחר מכן מחזירה את התוצאה של הקריאה ל-Send. קראו את פרטי הקשר מהקלט, צרו את שני טיפוסי השליחים והשתמשו בפונקציה שלכם כדי להעביר הודעות באמצעות כל אחד מהם.

הקלט הבא יסופק:

  • שורה 1: כתובת אימייל
  • שורה 2: מספר טלפון
  • שורה 3: הודעה לשליחה

המתודות Send שלכם צריכות להחזיר מחרוזות בפורמטים הבאים:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

לדוגמה, עבור alice@example.com,‏ 555-1234 ו-Hello!, הפלט שלכם צריך להיות:

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

התובנה המרכזית כאן היא שהפונקציה DeliverMessage שלכם מקבלת כל Messenger, וגם EmailMessenger וגם SMSMessenger תואמים לממשק הזה פשוט משום שיש להם מתודת Send תואמת. אין כאן שום מילת מפתח implements: המימוש המרומז של Go מטפל בכל זה.

נסו בעצמכם

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: צרו פונקציה בשם DeliverMessage שמקבלת כל Messenger
// ומחרוזת הודעה, ואז מחזירה את תוצאת הקריאה ל-Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// קראו את כתובת האימייל
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// קראו את מספר הטלפון
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// קראו את ההודעה
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: צרו EmailMessenger עם כתובת האימייל
	
	// TODO: צרו SMSMessenger עם מספר הטלפון
	
	// TODO: השתמשו ב-DeliverMessage כדי לשלוח את ההודעה דרך כל Messenger
	// והדפיסו את התוצאות
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין