Menu
Coddy logo textTech

Замыкания для приватности

Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 44 из 70.

Соглашения об именовании обозначают назначение, но на самом деле не предотвращают доступ. Любой всё ещё может написать player._health = 999, и это сработает. Для настоящей privacy Lua предлагает другой подход: хранить данные в переменных local внутри конструктора, а не в self.

Ключевая идея заключается в том, что переменные local внутри function доступны только внутри этой function и любых функций, определённых внутри неё. Это называется closure:

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 находится в области видимости closure. Она полностью скрыта от внешнего кода. (В текущем виде пока ничто не сохраняет count; в следующем уроке ты добавишь внутри конструктора методы, которые замкнут в себе эту переменную, поэтому она будет сохраняться столько же, сколько существует объект.) В отличие от self._count, что является лишь вежливым предложением, этот подход делает данные действительно недоступными.

Конечно, скрытые данные бесполезны, если вы никогда не можете с ними взаимодействовать. В следующем уроке вы узнаете, как определять методы внутри конструктора, которые могут читать и изменять эти private переменные, предоставляя вам контролируемый доступ и сохраняя truly инкапсуляцию.

challenge icon

Задание

Легко

Давайте создадим класс SecretKeeper, демонстрирующий настоящую конфиденциальность data с помощью closures! В отличие от соглашения с подчёркиванием из предыдущего урока, этот подход делает data действительно недоступными извне объекта.

Вы организуете свой код в двух файлах:

  • SecretKeeper.lua: Create class, в которой secret хранится в переменной local внутри конструктора, а не в self. Конструктор :new(secretValue) должен сохранить secret в переменной local, существующей только внутри closure. Пока у объекта не будет способа accessing этот secret (это появится в следующем уроке!), но вы также должны сохранить public поле name со значением "Keeper" в instance, чтобы мы могли проверить, что объект работает.
  • main.lua: Подключите модуль SecretKeeper с помощью require и прочитайте secret из input. Create instance SecretKeeper с этим secret. Затем продемонстрируйте, что secret truly private, выведя три значения:
    • поле name объекта keeper (должно вывести Keeper)
    • что happens при попытке accessing keeper.secret (результатом должно быть nil)
    • что happens при попытке accessing keeper._secret (результатом также должно быть nil)

Вы получите одно входное значение:

  1. значение secret для сохранения (например, TopSecret123)

Ваш output должен состоять из трёх строк, показывающих, что public поле работает, а secret полностью скрыт:

Name: Keeper
secret: nil
_secret: nil

Output будет одинаковым независимо от того, какой secret вы передадите, поскольку нет способа accessing его! Переменная local внутри конструктора невидима для внешнего мира. Это доказывает, что privacy на основе closure принципиально отличается от соглашения об именовании с подчёркиванием.

Например, если input — MyPassword, output всё равно должен быть таким:

Name: Keeper
secret: nil
_secret: nil

Попробуйте сами

-- Подключить модуль SecretKeeper
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