Closures לשמירה על פרטיות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Lua של Coddy. שיעור 44 מתוך 70.
מוסכמות שמות מסמנות כוונה, אבל הן לא באמת מונעות גישה. כל אחד עדיין יכול לכתוב player._health = 999 וזה יעבוד. כדי להשיג פרטיות אמיתית, Lua מציעה גישה אחרת: אחסון נתונים במשתנים מקומיים בתוך הבנאי במקום ב-self.
התובנה המרכזית היא שמשתנים מקומיים בתוך פונקציה נגישים רק בתוך אותה פונקציה ובתוך כל פונקציה שמוגדרת בתוכה. זה נקרא סגירה:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- פרטי באמת! לא נמצא ב-self.
local obj = {}
setmetatable(obj, Counter)
return obj
endבדוגמה הזו, count קיים רק בתוך הפונקציה :new(). הוא לא נשמר ב־obj, ולכן אין דרך לגשת אליו מבחוץ: myCounter.count מחזיר nil, ואין שדה שמתחיל בקו תחתון שאפשר לעקוף באמצעותו.
המשתנה count נמצא בתחום של הסגירה. הוא מוסתר לחלוטין מקוד חיצוני. (כפי שהקוד כתוב, עדיין שום דבר לא לוכד את count; בשיעור הבא תוסיף מתודות בתוך הבנאי שסוגרות עליו, כך שהוא יישאר כל עוד האובייקט קיים.) בניגוד ל־self._count, שהוא רק הצעה מנומסת, הגישה הזאת הופכת את הנתונים לבלתי נגישים באמת.
כמובן, נתונים מוסתרים אינם מועילים אם אי אפשר ליצור איתם אינטראקציה. בשיעור הבא תלמד איך להגדיר מתודות בתוך הבנאי שיכולות לקרוא את המשתנים הפרטיים האלה ולשנות אותם, וכך לאפשר לך גישה מבוקרת תוך שמירה על כימוס אמיתי.
אתגר
קלבואו נבנה מחלקה בשם SecretKeeper שמדגימה פרטיות אמיתית של נתונים באמצעות סגירות! בניגוד למוסכמת הקו התחתון מהשיעור הקודם, הגישה הזאת הופכת את הנתונים לבלתי נגישים באמת מחוץ לאובייקט.
תארגנו את הקוד שלכם בשני קבצים:
SecretKeeper.lua: צרו מחלקה שבה הסוד מאוחסן במשתנה מקומי בתוך הבנאי, ולא בתוךself. הבנאי:new(secretValue)צריך לאחסן את הסוד במשתנה מקומי שקיים רק בתוך הסגירה. בשלב זה, לא תהיה לאובייקט שום דרך לגשת לסוד הזה (על כך נלמד בשיעור הבא!), אבל עליכם גם לאחסן באובייקט שדה ציבורי בשםnameשערכו"Keeper", כדי שנוכל לוודא שהאובייקט עובד.main.lua: טענו את המודול SecretKeeper וקראו ערך סודי מהקלט. צרו מופע של SecretKeeper עם הסוד הזה. לאחר מכן הדגימו שהסוד אכן פרטי על ידי הדפסת שלושה דברים:- השדה
nameשל השומר (אמור להדפיסKeeper) - מה קורה כשמנסים לגשת אל
keeper.secret(אמור להיותnil) - מה קורה כשמנסים לגשת אל
keeper._secret(אמור גם להיותnil)
- השדה
תקבלו קלט אחד:
- הערך הסודי לאחסון (למשל,
TopSecret123)
הפלט שלכם צריך לכלול שלוש שורות שמראות שלמרות שהשדה הציבורי עובד, הסוד מוסתר לחלוטין:
Name: Keeper
secret: nil
_secret: nilהפלט יהיה זהה בלי קשר לסוד שתעבירו, כי אין דרך לגשת אליו! המשתנה המקומי בתוך הבנאי אינו גלוי לעולם החיצוני. הדבר מוכיח שפרטיות המבוססת על סגירות שונה מהותית ממוסכמת השמות עם קו תחתון.
לדוגמה, אם הקלט הוא MyPassword, הפלט עדיין צריך להיות:
Name: Keeper
secret: nil
_secret: nilנסו בעצמכם
-- טען את המודול SecretKeeper באמצעות require
local SecretKeeper = require('SecretKeeper')
-- קרא את הערך הסודי מהקלט
local secretValue = io.read()
-- TODO: צור מופע של SecretKeeper עם הערך הסודי
-- TODO: הדפס את השדה name של keeper (פורמט: "Name: <name>")
-- TODO: הדפס מה קורה כשניגשים ל־keeper.secret (פורמט: "secret: <value>")
-- TODO: הדפס מה קורה כשניגשים ל־keeper._secret (פורמט: "_secret: <value>")
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4פרויקט: בנק דיגיטלי
הגדרת הפרויקטפעולת הפקדה7פולימורפיזם ודריסת מתודות
דריסת מתודותקריאה למתודות של מחלקת האבטיפוסיות ברווזממשק משותףבדיקת טיפוססיכום – תפקידי עובדים2תבנית אב־טיפוס למחלקה
מושג אב־הטיפוסקישור באמצעות __indexהבנאי :new()אתחול מאפייניםמופעים עצמאייםסיכום – מפעל מכוניות5העמסת אופרטורים בתכנות מונחה עצמים
חיבור אובייקטיםחיסור אובייקטיםשרשור אובייקטיםהשוואת אובייקטים (<, >)חזרה – חישובים עם ארנק8כימוס
כללי מתן שמותClosures לשמירה על פרטיותגישה באמצעות Closuresטבלאות לקריאה בלבדלוגיקת אימותסיכום – כספת מאובטחת11תבניות עיצוב (בקצרה)
פונקציות Factoryטבלת Singletonתבנית IteratorObserver (מאזין)סיכום – מפעל Logger3מצב והתנהגות של אובייקטים
משתני מופעמתודות Getterמתודות Setterמאפיינים מחושביםעיצוב מחרוזותבדיקות שוויוןסיכום – ציון תלמיד6יסודות הירושה
הכנת מבנה הירושהירושת מתודותהרחבת הבנאיהוספת מתודות למחלקת הבתמשותף לעומת ייחודיסיכום – היררכיית צורותתרגלו בעצמכם: קומפיילר Lua אונליין