הסתרת מידע
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Dart של Coddy. שיעור 34 מתוך 110.
הסתרת מידע היא העיקרון שמאחורי כימוס — הרעיון שמחלקה צריכה להסתיר את אופן פעולתה הפנימי ולחשוף רק את מה שנחוץ. למדת את המנגנונים (שדות פרטיים, פונקציות גישה ופונקציות קביעה), אבל המטרה האמיתית היא לתכנן מחלקות שקל להשתמש בהן וקשה להשתמש בהן באופן שגוי.
נבחן מחלקה שמנהלת את הסיסמה של משתמש. לוגיקת האחסון והגיבוב הפנימית צריכה להיות מוסתרת לחלוטין:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // דוגמה פשוטה
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}קוד חיצוני לעולם אינו רואה את גיבוב הסיסמה או יודע כיצד הוא מאוחסן. הוא פשוט קורא ל־verifyPassword(). ההפרדה הזאת פירושה שתוכל לשנות לחלוטין את אלגוריתם הגיבוב מבלי להשפיע על שום קוד שמשתמש במחלקה הזאת.
התובנה המרכזית היא לחשוב על המחלקה שלך מבחוץ פנימה.
שאלו את עצמכם: "מה באמת נחוץ למי שמשתמש במחלקה הזאת?" חשפו רק את זה, והשאירו את כל השאר פרטי. כך נוצר גבול ברור בין מה שהמחלקה שלכם עושה (הממשק הציבורי שלה) לבין האופן שבו היא עושה זאת (המימוש הפרטי שלה).
אתגר
קלבואו נבנה מערכת ארנק מאובטחת שמדגימה הסתרת מידע בפעולה. תיצרו ארנק דיגיטלי שמנהל יתרה והיסטוריית עסקאות של משתמש, תוך הסתרה מוחלטת של כל הפרטים הפנימיים הרגישים מקוד חיצוני.
תארגנו את הקוד בשני קבצים:
wallet.dart: צרו מחלקה בשםWalletשמנהלת כסף באופן מאובטח. הארנק צריך לאחסן את שם הבעלים באופן ציבורי, אך כל השאר צריך להיות מוסתר מאחורי ממשק ציבורי פשוט:- היתרה הפנימית ויומן העסקאות צריכים להיות פרטיים — קוד חיצוני לא אמור לראות לעולם איך אתם מאחסנים או עוקבים אחר כסף
- מתודה פרטית צריכה לטפל ברישום עסקאות באופן פנימי (ולאחסן רשומות כמו
"Deposit: $50.0"או"Withdrawal: $30.0") - ספקו מתודה ציבורית
deposit(double amount)שמוסיפה כסף (רק אם הסכום חיובי) ומתעדת את העסקה באופן פנימי - ספקו מתודה ציבורית
withdraw(double amount)שמוציאה כסף רק אם יש מספיק כספים והסכום חיובי, ומתעדת את העסקה באופן פנימי. החזירוtrueאם הפעולה הצליחה, ואחרתfalse - ספקו getter ציבורי
balanceשמחזיר את היתרה הנוכחית (גישה לקריאה בלבד) - ספקו מתודה ציבורית
getTransactionCount()שמחזירה כמה עסקאות תועדו (בלי לחשוף את רשימת העסקאות בפועל) - ספקו מתודה ציבורית
getLastTransaction()שמחזירה את מחרוזת העסקה האחרונה, או"No transactions"אם אין עסקאות - כללו מתודה
printSummary()שמציגה את מצב הארנק
main.dart: ייבאו את הארנק והדגימו איך הסתרת מידע יוצרת ממשק פשוט ובטוח:- צרו ארנק עבור הבעלים
"Sarah" - הפקידו
100.0 - הפקידו
50.0 - נסו למשוך
200.0והדפיסו"Withdrawal of 200.0: [success/failed]"בהתאם לתוצאה - משכו
30.0והדפיסו"Withdrawal of 30.0: [success/failed]" - קראו ל־
printSummary() - הדפיסו
"Transaction count: [count]" - הדפיסו
"Last transaction: [transaction]"
- צרו ארנק עבור הבעלים
המתודה printSummary() צריכה להדפיס:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]שימו לב איך קוד חיצוני יכול לקיים אינטראקציה עם הארנק באמצעות מתודות ברורות וממוקדות, בלי לדעת לעולם איך היתרה מאוחסנת או איך העסקאות מנוהלות באופן פנימי. פרטי המימוש מוסתרים לחלוטין — תוכלו לשנות את אופן אחסון העסקאות בלי להשפיע על שום קוד שמשתמש בארנק.
הפלט הצפוי:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0נסו בעצמכם
import 'wallet.dart';
void main() {
// TODO: צור ארנק עבור הבעלים "Sarah"
// TODO: הפקד 100.0
// TODO: הפקד 50.0
// TODO: נסה למשוך 200.0 והדפס את התוצאה
// פורמט: "Withdrawal of 200.0: [success/failed]"
// TODO: משוך 30.0 והדפס את התוצאה
// פורמט: "Withdrawal of 30.0: [success/failed]"
// TODO: קרא ל־printSummary()
// TODO: הדפס את מספר העסקאות
// פורמט: "Transaction count: [count]"
// TODO: הדפס את העסקה האחרונה
// פורמט: "Last transaction: [transaction]"
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
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בנאים אסינכרונייםאסינכרוניות במתודות של מחלקהסיכום - אחזור נתוניםתרגלו בעצמכם: קומפיילר Dart אונליין