תבנית Abstract Factory
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 88 מתוך 107.
תבנית המפעל המופשט מרחיבה את תבנית המפעל באמצעות יצירת משפחות של אובייקטים קשורים בלי לציין את הסוגים הקונקרטיים שלהם. במקום פונקציית מפעל יחידה, מגדירים ממשק מפעל שכמה מפעלים קונקרטיים מממשים, וכל אחד מהם מייצר קבוצה עקבית של אובייקטים קשורים.
נניח שיש ערכת כלים לממשק משתמש שצריכה ליצור כפתורים ותיבות סימון. מערכות הפעלה שונות דורשות מימושים שונים, אבל הרכיבים חייבים לעבוד יחד באופן עקבי:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// ממשק מפעל מופשט
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}כל מפעל קונקרטי מייצר משפחה של רכיבים קשורים:
// משפחת Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// משפחת Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }קוד הלקוח עובד עם ממשק המפעל ונשאר בלתי תלוי במימושים קונקרטיים:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}השתמשו ב-Abstract Factory כאשר המערכת שלכם צריכה לעבוד עם כמה משפחות של מוצרים קשורים, ואתם רוצים לוודא שרכיבים מאותה משפחה ישמשו יחד. היא מורכבת יותר מ-Factory פשוטה, לכן השתמשו בה רק כשבאמת צריך לנהל משפחות מוצרים.
אתגר
קלבואו נבנה מערכת לחנות רהיטים באמצעות תבנית Abstract Factory! תיצרו מפעלים שמייצרים משפחות של פריטי ריהוט קשורים: כל מפעל מייצר סגנון אחיד (מודרני או וינטג׳), וכך מובטח שכיסאות ושולחנות מאותו מפעל יתאימו זה לזה מבחינה אסתטית.
תארגנו את הקוד שלכם בשלושה קבצים:
furniture.go: הגדירו את ממשקי המוצרים ואת סוגי הרהיטים הקונקרטיים.צרו שני ממשקי מוצרים:
Chairעם מתודהSitOn() stringTableעם מתודהPlaceItems() string
ממשו שתי משפחות רהיטים:
- משפחה מודרנית:
ModernChairמחזירהSitting on a sleek modern chairו-ModernTableמחזירהPlacing items on a glass modern table - משפחת וינטג׳:
VintageChairמחזירהSitting on an ornate vintage chairו-VintageTableמחזירהPlacing items on a wooden vintage table
factory.go: הגדירו את ממשק המפעל המופשט ואת המפעלים הקונקרטיים.צרו ממשק
FurnitureFactoryעם שתי מתודות:CreateChair() ChairCreateTable() Table
ממשו שני מפעלים קונקרטיים:
ModernFactoryשמייצרModernChairו-ModernTableVintageFactoryשמייצרVintageChairו-VintageTable
צרו גם פונקציה
GetFactory(style string) FurnitureFactoryשמחזירה את המפעל המתאים בהתאם לסגנון (modernאוvintage). עבור סגנונות לא מזוהים, ברירת המחדל תהיהModernFactory.main.go: השתמשו במפעלים שלכם כדי לרהט חדרים.צרו פונקציה
FurnishRoom(factory FurnitureFactory) stringשמשתמשת במפעל כדי ליצור כיסא ושולחן, ואז מחזירה מחרוזת מעוצבת שמציגה את שתי הפעולות בשורות נפרדות.קראו קלט של סגנון, קבלו את המפעל המתאים, רהטו חדר והדפיסו את התוצאה.
הקלטים הבאים יסופקו:
- שורה 1: סגנון הריהוט (
modern,vintageאו ערך אחר)
לדוגמה, בהינתן:
modernהפלט שלכם צריך להיות:
Sitting on a sleek modern chair
Placing items on a glass modern tableובהינתן:
vintageהפלט שלכם צריך להיות:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableובהינתן:
rusticהפלט שלכם צריך להיות:
Sitting on a sleek modern chair
Placing items on a glass modern tableשימו לב שפונקציית FurnishRoom פועלת עם כל מפעל באמצעות הממשק. היא לא יודעת אם היא יוצרת רהיטים מודרניים או רהיטי וינטג׳. כך מובטח שכל הרהיטים בחדר מגיעים מאותה משפחה ושומרים על סגנון אחיד!
נסו בעצמכם
package main
import "fmt"
// FurnishRoom משתמשת במפעל ליצירת רהיטים ומחזירה את התוצאה
// TODO: ממשו את הפונקציה הזו
// - השתמשו במפעל כדי ליצור כיסא ושולחן
// - החזירו מחרוזת עם התוצאה של SitOn() בשורה הראשונה והתוצאה של PlaceItems() בשורה השנייה
func FurnishRoom(factory FurnitureFactory) string {
// TODO: צרו כיסא ושולחן באמצעות המפעל
// TODO: החזירו מחרוזת מעוצבת עם שתי הפעולות בשורות נפרדות
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: קבלו את המפעל המתאים לפי style
// TODO: רהטו חדר באמצעות המפעל
// TODO: הדפיסו את התוצאה
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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 אונליין