Menu
Coddy logo textTech

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, שהוא רק הצעה מנומסת, הגישה הזאת הופכת את הנתונים לבלתי נגישים באמת.

כמובן, נתונים מוסתרים אינם מועילים אם אי אפשר ליצור איתם אינטראקציה. בשיעור הבא תלמד איך להגדיר מתודות בתוך הבנאי שיכולות לקרוא את המשתנים הפרטיים האלה ולשנות אותם, וכך לאפשר לך גישה מבוקרת תוך שמירה על כימוס אמיתי.

challenge icon

אתגר

קל

בואו נבנה מחלקה בשם SecretKeeper שמדגימה פרטיות אמיתית של נתונים באמצעות סגירות! בניגוד למוסכמת הקו התחתון מהשיעור הקודם, הגישה הזאת הופכת את הנתונים לבלתי נגישים באמת מחוץ לאובייקט.

תארגנו את הקוד שלכם בשני קבצים:

  • SecretKeeper.lua: צרו מחלקה שבה הסוד מאוחסן במשתנה מקומי בתוך הבנאי, ולא בתוך self. הבנאי :new(secretValue) צריך לאחסן את הסוד במשתנה מקומי שקיים רק בתוך הסגירה. בשלב זה, לא תהיה לאובייקט שום דרך לגשת לסוד הזה (על כך נלמד בשיעור הבא!), אבל עליכם גם לאחסן באובייקט שדה ציבורי בשם name שערכו "Keeper", כדי שנוכל לוודא שהאובייקט עובד.
  • main.lua: טענו את המודול SecretKeeper וקראו ערך סודי מהקלט. צרו מופע של SecretKeeper עם הסוד הזה. לאחר מכן הדגימו שהסוד אכן פרטי על ידי הדפסת שלושה דברים:
    • השדה name של השומר (אמור להדפיס Keeper)
    • מה קורה כשמנסים לגשת אל keeper.secret (אמור להיות nil)
    • מה קורה כשמנסים לגשת אל keeper._secret (אמור גם להיות nil)

תקבלו קלט אחד:

  1. הערך הסודי לאחסון (למשל, 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>")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Lua אונליין