Menu
Coddy logo textTech

הטמעה לעומת אגרגציה

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

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

בהטמעה, משמיטים את שם השדה, והחברים של הטיפוס הפנימי הופכים לנגישים ישירות:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // הטמעה - ללא שם שדה
}

d := Document{}
d.Write()  // גישה ישירה

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

type Document struct {
    writer Writer  // אגרגציה - שדה בעל שם
}

d := Document{writer: Writer{}}
d.writer.Write()  // יש להשתמש בשם השדה

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

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

challenge icon

אתגר

קל

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

תארגן את הקוד שלך בשלושה קבצים:

  • formatter.go: צור מבנה Formatter עם שדה Style (מחרוזת) ומתודה Format(text string) string שמחזירה את הטקסט כשהוא עטוף בסגנון, כמו [Style] text [/Style]. זה יהיה כלי עזר פנימי שלא אמור להיות חשוף ישירות.
  • report.go: צור שני מבנים שמטפלים ביצירת דוחות בדרכים שונות:
    • מבנה Metadata עם השדות Author ו-Date. הוא מייצג מידע ציבורי שאמור להיות נגיש ישירות
    • מבנה Report עם שדה Title שמטמיע את Metadata (כדי לאפשר גישה ישירה למחבר ולתאריך), אך משתמש בצבירה עבור Formatter (כפרט מימוש פנימי עם שדה בעל שם formatter)
    הוסף למבנה Report מתודה Generate() string שמשתמשת במעצב הפנימי כדי לעצב את הכותרת ולכלול את המטא-נתונים.
  • main.go: קרא את פרטי הדוח מהקלט, וצור Report עם מטא-נתונים מוטמעים וגם מעצב מצטבר. הדגם את ההבדל באמצעות גישה ישירה למחבר מתוך הדוח (דרך ההטמעה), ולאחר מכן יצירת הדוח המעוצב (שמשתמש במעצב המוסתר באופן פנימי).

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

  • שורה 1: כותרת הדוח
  • שורה 2: שם המחבר
  • שורה 3: תאריך
  • שורה 4: סגנון העיצוב

המתודה Format שלך צריכה להחזיר:

[Style] text [/Style]

המתודה Generate שלך צריכה להחזיר:

[formatted title]
Author: [Author], Date: [Date]

בקובץ הראשי שלך, הדפס שלוש שורות:

  1. המחבר שאליו ניגשים ישירות מתוך Report (כדי להדגים שההטמעה מקדמת את השדה)
  2. התאריך שאליו ניגשים דרך שם הטיפוס המוטמע (report.Metadata.Date)
  3. התוצאה של קריאה ל-Generate()

לדוגמה, עבור Quarterly Results, Jane Doe, 2024-01-15 ו-BOLD, הפלט שלך אמור להיות:

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

שימו לב שאפשר לגשת ל-Author ול-Date ישירות מתוך Report באמצעות הטמעה, בעוד שהמעצב נשאר מוסתר. אי אפשר לקרוא ישירות ל-report.Format(), משום שהוא נצבר באמצעות שדה בעל שם. כך מודגמים בפועל היחסים "הוא-סוג-של" לעומת "יש-לו".

נסו בעצמכם

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: צרו struct מסוג Metadata עם המחבר והתאריך
	
	// TODO: צרו Formatter עם הסגנון הנתון
	
	// TODO: צרו Report שמטמיע את Metadata ומכיל את Formatter
	
	// TODO: הדפיסו את המחבר באמצעות גישה ישירה דרך Report (ההטמעה מקדמת את השדה)
	
	// TODO: הדפיסו את התאריך באמצעות גישה דרך שם הטיפוס המוטמע (report.Metadata.Date)
	
	// TODO: הדפיסו את התוצאה של הקריאה ל־Generate()
	
	// מנעו אזהרות על משתנים שאינם בשימוש (הסירו את השורות האלה כשתשתמשו במשתנים)
	_ = title
	_ = author
	_ = date
	_ = style
}
quiz iconבחנו את עצמכם

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

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

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