תבנית Factory
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 87 מתוך 107.
תבנית Factory מעבירה את יצירת האובייקטים לפונקציה או למתודה נפרדת, ומאפשרת לך ליצור מופעים בלי לחשוף את לוגיקת היצירה. בניגוד ל-Singleton, ששולטת במספר המופעים, Factory מתמקדת ביצירה גמישה של אובייקטים על סמך פרמטרי הקלט.
ב-Go, factory היא בדרך כלל פונקציה שמחזירה טיפוס ממשק, ומאפשרת ליצור טיפוסים קונקרטיים שונים באמצעות API אחיד:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}הפונקציה היוצרת NewNotifier עוטפת את ההחלטה איזה טיפוס קונקרטי ליצור. קוד הלקוח עובד רק עם הממשק Notifier, ואינו מודע למימוש הספציפי:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"התבנית הזאת מצטיינת כשצריך ליצור אובייקטים על סמך תנאים בזמן ריצה, לרכז לוגיקת אתחול מורכבת, או להוסיף בקלות טיפוסים חדשים בלי לשנות את קוד הלקוח. פשוט מוסיפים struct חדש שמממש את הממשק ומעדכנים את פונקציית המפעל.
אתגר
קלבואו נבנה מערכת להמרת מסמכים באמצעות תבנית Factory! תיצרו factory שמייצר ממירי מסמכים שונים בהתאם לפורמט היעד, וכך קוד הלקוח יוכל לעבוד עם כל ממיר דרך ממשק אחיד.
תארגנו את הקוד בשני קבצים:
converter.go: הגדירו את ממשק הממיר ואת המימושים הקונקרטיים.צרו ממשק
Converterעם מתודהConvert(content string) stringשמקבלת את תוכן המסמך ומחזירה את הפלט שהומר.ממשו שלושה סוגי ממירים:
PDFConverter: המתודהConvertשלו מחזירהPDF: [content]HTMLConverter: המתודהConvertשלו מחזירהHTML: <p>[content]</p>MarkdownConverter: המתודהConvertשלו מחזירהMD: # [content]
צרו פונקציית factory בשם
NewConverter(format string) Converterשמחזירה את הממיר המתאים בהתאם למחרוזת הפורמט (pdf,htmlאוmarkdown). עבור כל פורמט לא מזוהה, החזירוPDFConverterכברירת מחדל.main.go: השתמשו ב-factory כדי ליצור ממירים ולעבד מסמכים.קראו סוג פורמט ותוכן של מסמך. השתמשו ב-factory כדי ליצור את הממיר המתאים, לאחר מכן המירו את התוכן והדפיסו את התוצאה.
הקלטים הבאים יסופקו:
- שורה 1: סוג הפורמט (
pdf,html,markdownאו משהו אחר) - שורה 2: תוכן המסמך להמרה
לדוגמה, עבור:
html
Welcome to Go Programmingהפלט שלכם אמור להיות:
HTML: <p>Welcome to Go Programming</p>ועבור:
markdown
Chapter Oneהפלט שלכם אמור להיות:
MD: # Chapter Oneועבור:
pdf
Annual Report 2024הפלט שלכם אמור להיות:
PDF: Annual Report 2024ועבור:
docx
Meeting Notesהפלט שלכם אמור להיות:
PDF: Meeting Notesשימו לב שהקוד הראשי לא צריך להכיר את סוגי הממירים הספציפיים. הוא פשוט מבקש ממיר מה-factory ומשתמש בממשק. כך קל להוסיף פורמטים חדשים בהמשך בלי לשנות את קוד הלקוח!
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// קרא את סוג הפורמט
scanner.Scan()
format := scanner.Text()
// קרא את תוכן המסמך
scanner.Scan()
content := scanner.Text()
// TODO: השתמש במפעל ליצירת הממיר המתאים
// TODO: המר את התוכן והדפס את התוצאה
fmt.Println(result)
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי13תבניות עיצוב חלק 1
מבוא לתבניות עיצובתבנית Singletonתבנית Factoryתבנית Abstract Factoryתבנית Observerתבנית Strategy2העמקה בסוגים וב-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 אונליין