שכבת התמדה עם JSON
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 102 מתוך 107.
אתגר
קלנוסיף התמדה של נתונים למערכת ניהול הספרייה שלנו! תממשו שכבת התמדה של JSON שיכולה להמיר את נתוני הספרייה שלכם לפורמט JSON לשמירה, ולטעון אותם בחזרה ממחרוזות JSON. זה חיוני לכל יישום אמיתי שצריך לשמר נתונים בין הפעלות.
תארגנו את הקוד שלכם בחמישה קבצים:
book.go: המבנהBookשלכם עם תגיות JSON למבנה, כדי לשלוט בסריאליזציה. לכל שדה צריכה להיות תגית JSON מתאימה:id,title,author,isbnו-available. כללו את הבנאיNewBookשלכם.user.go: המבנהUserשלכם עם תגיות JSON למבנה עבורid,name,emailו-borrowed_books. כללו את הבנאיNewUserשלכם.data.go: צרו מבנהLibraryDataשמשמש כאובייקט העברה לסריאליזציה של JSON. צריכים להיות לו שני שדות:Books(פרוסה של מצביעים ל-Book) ו-Users(פרוסה של מצביעים ל-User), כל אחד עם תגיות JSON מתאימות (booksו-users).library.go: המבנהLibraryשלכם עם מפות לספרים ולמשתמשים. הוסיפו שתי מתודות חדשות:ToJSON(): ממירה את נתוני הספרייה למחרוזת JSON מעוצבת באמצעותjson.MarshalIndentעם הזחה של שני רווחים. עליה לאסוף את כל הספרים והמשתמשים מהמפות לתוך מבנהLibraryData, ואז לבצע סריאליזציה שלו.FromJSON(jsonStr string): מקבלת מחרוזת JSON, מבצעת לה unmarshaling לתוך מבנהLibraryData, וממלאת את מפות הספרייה בספרים ובמשתמשים שנטענו.
main.go: הדגימו את שכבת ההתמדה באמצעות בניית ספרייה והמרתה ל-JSON.קראו את מספר הספרים, ואז עבור כל ספר קראו את המזהה שלו, הכותרת, המחבר וה-ISBN. לאחר מכן, קראו את מספר המשתמשים, ועבור כל משתמש קראו את המזהה שלו, השם וכתובת הדוא"ל. לאחר מילוי הספרייה, קראו ל-
ToJSON()והדפיסו את מחרוזת ה-JSON שהתקבלה.
הקלטים הבאים יסופקו:
- מספר הספרים, ואז המזהה, הכותרת, המחבר וה-ISBN של כל ספר (כל אחד בשורה נפרדת)
- מספר המשתמשים, ואז המזהה, השם וכתובת הדוא"ל של כל משתמש (כל אחד בשורה נפרדת)
לדוגמה, עבור:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.comהפלט שלכם צריך להיות:
{
"books": [
{
"id": "B001",
"title": "The Go Programming Language",
"author": "Alan Donovan",
"isbn": "978-0134190440",
"available": true
},
{
"id": "B002",
"title": "Clean Code",
"author": "Robert Martin",
"isbn": "978-0132350884",
"available": true
}
],
"users": [
{
"id": "U001",
"name": "Alice Smith",
"email": "alice@library.com",
"borrowed_books": []
}
]
}ועבור:
1
B100
Design Patterns
Gang of Four
978-0201633610
0הפלט שלכם צריך להיות:
{
"books": [
{
"id": "B100",
"title": "Design Patterns",
"author": "Gang of Four",
"isbn": "978-0201633610",
"available": true
}
],
"users": []
}חשוב: כשאתם עוברים על המפות כדי לבנות את הפרוסות, הוסיפו ספרים לפי הסדר: B001, B002 וכן הלאה (ממוינים לפי מזהה בסדר אלפביתי), וכך גם לגבי המשתמשים. כך תבטיחו פלט JSON עקבי. תוכלו להשתמש ב-sort.Strings על מפתחות המפה לפני המעבר עליהם.
המבנה LibraryData מגשר על הפער בין האחסון שלכם המבוסס על מפות לבין הפורמט של JSON המבוסס על מערכים. תבנית אובייקט ההעברה הזו נפוצה בעת סריאליזציה של מבני נתונים מורכבים!
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// צור ספרייה חדשה
library := NewLibrary()
// קרא את מספר הספרים
numBooksStr, _ := reader.ReadString('\n')
numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))
// TODO: קרא את הפרטים של כל ספר (ID, כותרת, מחבר, ISBN)
// והוסף אותם לספרייה
for i := 0; i < numBooks; i++ {
// TODO: קרא את ה-ID של הספר
// TODO: קרא את כותרת הספר
// TODO: קרא את שם מחבר הספר
// TODO: קרא את ה-ISBN של הספר
// TODO: צור ספר והוסף אותו לספרייה
}
// קרא את מספר המשתמשים
numUsersStr, _ := reader.ReadString('\n')
numUsers, _ := strconv.Atoi(strings.TrimSpace(numUsersStr))
// TODO: קרא את הפרטים של כל משתמש (ID, שם, דוא״ל)
// והוסף אותם לספרייה
for i := 0; i < numUsers; i++ {
// TODO: קרא את ה-ID של המשתמש
// TODO: קרא את שם המשתמש
// TODO: קרא את כתובת האימייל של המשתמש
// TODO: צור משתמש והוסף אותו לספרייה
}
// TODO: המר את הספרייה ל-JSON והדפס אותה
fmt.Println(library.ToJSON())
}
כל השיעורים ביחידה תכנות מונחה עצמים
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סקירה כללית של יצירת קוד15פרויקט: ניהול ספרייה
סקירה כללית ומבנה הפרויקטמבני נתונים של ספרים ומשתמשיםתרגלו בעצמכם: קומפיילר Go אונליין