תבנית המאגר
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Dart של Coddy. שיעור 100 מתוך 110.
תבנית Repository מפרידה בין לוגיקת הגישה לנתונים לבין הלוגיקה העסקית באמצעות יצירת שכבת הפשטה בין היישום שלך לבין מקורות הנתונים. במקום שהמחלקות שלך יתקשרו ישירות עם מסדי נתונים, ממשקי API או קבצים, הן עובדות עם מאגר שמטפל בכל פעולות הנתונים.
התבנית מגדירה ממשק לפעולות נתונים, ומימושים קונקרטיים מטפלים במנגנון האחסון בפועל. כך אפשר להחליף בקלות מקורות נתונים בלי לשנות את הלוגיקה העסקית שלך:
// ישות
class User {
final int id;
final String name;
User(this.id, this.name);
}
// ממשק מאגר
abstract class UserRepository {
User? getById(int id);
List<User> getAll();
void save(User user);
void delete(int id);
}
// מימוש בזיכרון
class InMemoryUserRepository implements UserRepository {
final Map<int, User> _users = {};
@override
User? getById(int id) => _users[id];
@override
List<User> getAll() => _users.values.toList();
@override
void save(User user) => _users[user.id] = user;
@override
void delete(int id) => _users.remove(id);
}
void main() {
UserRepository repo = InMemoryUserRepository();
repo.save(User(1, 'Alice'));
repo.save(User(2, 'Bob'));
print(repo.getById(1)?.name); // Alice
print(repo.getAll().length); // 2
}הלוגיקה העסקית שלך תלויה רק בממשק UserRepository. בהמשך תוכל ליצור DatabaseUserRepository או ApiUserRepository שמממשים את אותו ממשק, ולהחליף בין המימושים בלי לשנות שום קוד שמשתמש במאגר. הדפוס הזה חיוני ליישומים שאפשר לבדוק ולתחזק, שבהם מקורות הנתונים עשויים להשתנות עם הזמן.
אתגר
קלבואו נבנה מערכת מלאי מוצרים באמצעות תבנית Repository! תיצרו שכבת הפשטה שמפרידה בין אופן אחסון המוצרים לבין אופן השימוש בהם, וכך יהיה קל להחליף מימושי אחסון בלי לשנות את הלוגיקה העסקית.
תארגנו את הקוד בשני קבצים:
product_repository.dart: הקובץ הזה מכיל את מחלקות הישות והמאגר. התחילו במחלקהProductעם שדותfinalעבורid(int),name(String) ו-price(double). לאחר מכן הגדירו מחלקה מופשטת בשםProductRepositoryשמצהירה על החוזה לפעולות נתונים:getById(int id)שמחזירהProductשיכול להיות null,getAll()שמחזירה רשימת מוצרים,save(Product product)לשמירת מוצר, ו-delete(int id)להסרת מוצר. לבסוף, צרוInMemoryProductRepositoryשמממשת את הממשק הזה באמצעותMap<int, Product>לאחסון.main.dart: ייבאו את קובץ המאגר והדגימו את תבנית Repository בפעולה. צרוInMemoryProductRepository, אבל אחסנו אותו במשתנה מהסוגProductRepository— כך תראו שהקוד שלכם תלוי בממשק ולא במימוש. שמרו שלושה מוצרים:Laptop(id: 1, price: 999.99),Mouse(id: 2, price: 29.99) ו-Keyboard(id: 3, price: 79.99). לאחר מכן שלפו והדפיסו את המוצר עם המזהה 2 בפורמטFound: [name] - $[price]. מחקו את המוצר עם המזהה 1. לבסוף, הדפיסו את מספר המוצרים שנותרו בפורמטProducts in stock: [count].
היתרון של התבנית הזו הוא שהקוד הראשי שלכם מכיר רק את הממשק ProductRepository. בהמשך תוכלו ליצור DatabaseProductRepository או ApiProductRepository ולהחליף אליו בלי לשנות אף חלק מהלוגיקה העסקית!
פלט צפוי:
Found: Mouse - $29.99
Products in stock: 2נסו בעצמכם
import 'product_repository.dart';
void main() {
// צרו מאגר מוצרים - שימו לב שהטיפוס הוא הממשק, לא המימוש
// TODO: צרו InMemoryProductRepository ושמרו אותו במשתנה מסוג ProductRepository
// TODO: שמרו שלושה מוצרים:
// - Laptop (id: 1, price: 999.99)
// - Mouse (id: 2, price: 29.99)
// - Keyboard (id: 3, price: 79.99)
// TODO: אחזרו את המוצר עם id 2 והדפיסו: "Found: [name] - $[price]"
// TODO: מחקו את המוצר עם id 1
// TODO: הדפיסו את מספר המוצרים שנותרו: "Products in stock: [count]"
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםספריות וייבואמבוא לתכנות מונחה עצמיםמחלקות לעומת אובייקטיםמילת המפתח thisמתודותמשתני מופעיסודות הבנאיחזרה: מחשבון פשוט4בטיחות מפני ערכי null
מבוא לבטיחות מפני ערכי nullניתן להקצאה ל-null לעומת לא ניתן להקצאה ל-nullהאופרטורים ? ו-!מילת המפתח Late ובטיחות מפני ערכי nullאופרטורים מודעי nullבטיחות מפני ערכי null במחלקותסיכום – מערכת פרופילי משתמשים7מחלקות מופשטות וממשקים
מחלקות מופשטותמתודות מופשטותממשקים ב-Dartממשקים משתמעיםמימוש לעומת הורשהממשקים מרוביםחזרה – מחשבון צורות10אוספים וגנריות
סקירה כללית של List, Set ו-Mapאוספים בטוחים מבחינת טיפוסיםמחלקות גנריותמתודות גנריותאילוצים גנרייםIterable ו-Iteratorסיכום – אחסון גנרי13מושגי OOP מתקדמים
הרכבה לעומת הורשהמתודות הרחבהמחלקות קריאותמחלקות אטומות (Dart 3)רשומות (Dart 3)תבניות והתאמה (3.0)טיפוסים מונים עם מתודות16פרויקט: ניהול ספרייה
סקירת הפרויקטמחלקות ספר ומשתמש2בנאים ב-Dart
בנאי ברירת מחדלבנאים בעלי שםרשימות אתחולבנאים קבועיםבנאי Factoryבנאים מפניםסיכום - בונה צורות5כימוס
חברים ציבוריים לעומת פרטייםמוסכמת הקידומת _פרטיות ברמת הספרייההעמקה ב-getters וב-settersהסתרת מידעסיכום – רשומות תלמידים8מיקסינים
מבוא למיקסיניםיצירת מיקסיניםשימוש במספר מיקסיניםמילת המפתח on במיקסיניםמיקסינים לעומת ירושהמיקסינים לעומת ממשקסיכום – מערכת בעלי החיים3מאפייני מחלקה
איברי מופע לעומת איברים סטטייםשדות final ו-constמשתני lateמתודות ושדות סטטייםGetters ו-Settersסיכום – מנהל חשבונות בנק6ירושה
ירושה בסיסיתמילת המפתח superדריסת מתודותהאנוטציה @overrideמילת המפתח final למחלקהבנאים וירושהסיכום – היררכיית עובדים9פולימורפיזם
יסודות הפולימורפיזםפולימורפיזם באמצעות ממשקיםבדיקת טיפוסים בזמן ריצההאופרטורים is ו-asמילת המפתח קווריאנטיסיכום — מעבד תשלומים12תכנות מונחה עצמים אסינכרוני
Future-ים ו-async/awaitיסודות ה-Streamsבקרי Streamsבנאים אסינכרונייםאסינכרוניות במתודות של מחלקהסיכום - אחזור נתונים15תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובןתבנית שיטת התבניתתבנית המצבתבנית המרוכבתבנית המאגרתרגלו בעצמכם: קומפיילר Dart אונליין