클로저를 통한 접근
Coddy Lua 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 70개 중 45번째.
비공개 지역 변수는 그것들과 상호작용할 수 없다면 아무런 소용이 없습니다. 해결책은 클로저의 스코프에 접근할 수 있는 생성자 내부에 게터(getter)와 세터(setter) 메서드를 정의하는 것입니다.
이러한 메서드들은 :new() 내부에서 생성되므로 지역 변수들을 "볼" 수 있습니다. 이들을 객체에 직접 할당합니다:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- 프라이빗 변수
local obj = {}
-- 생성자 내부에서 메서드 정의
function obj:getCount()
return count
end
function obj:increment()
count = count + 1
end
setmetatable(obj, Counter)
return obj
end이제 프라이빗 count 변수에 접근할 수 있지만, 오직 제공된 메서드를 통해서만 가능합니다:
local c = Counter:new()
print(c:getCount()) -- 0
c:increment()
c:increment()
print(c:getCount()) -- 2
print(c.count) -- nil (여전히 숨겨져 있음!):getCount()와 :increment()가 클래스 테이블이 아니라 obj에 직접 정의되어 있다는 점에 유의하세요. 각 인스턴스는 이 함수들의 개별 복사본을 갖게 되며, 각각 자신의 비공개 count에 접근할 수 있습니다. 이것이 트레이드오프(trade-off)입니다. 메서드가 프로토타입을 통해 공유되지 않기 때문에 진정한 프라이버시를 구현하려면 메모리가 조금 더 소모됩니다.
챌린지
쉬움클로저 기반 프라이버시를 사용하여 섭씨 온도를 저장하고, 생성자 내부에 정의된 게터(getter) 및 세터(setter) 메서드를 통해 제어된 접근을 제공하는 Temperature 클래스를 만들어 봅시다!
코드는 다음 두 개의 파일로 구성됩니다:
Temperature.lua: 온도 값이 생성자 내부의 지역 변수에 저장되어 외부 접근으로부터 완전히 숨겨진 클래스를 생성합니다. 생성자:new(initialCelsius)는 온도를 지역 변수에 저장한 다음, 객체 인스턴스에 직접 다음 메서드들을 정의해야 합니다::getCelsius()— 현재 섭씨 온도를 반환합니다.:setCelsius(value)— 온도를 새로운 값으로 업데이트합니다.:getFahrenheit()—(celsius * 9/5) + 32공식을 사용하여 섭씨 온도를 화씨로 변환하여 반환합니다.
main.lua: Temperature 모듈을 불러오고(require) 입력으로부터 두 개의 값(초기 온도와 새로 설정할 온도)을 읽습니다. 초기 값으로 Temperature 인스턴스를 생성하고, 섭씨 및 화씨 온도를 출력한 다음, 세터를 사용하여 온도를 변경하고 두 온도를 다시 출력합니다.
두 개의 입력을 받게 됩니다:
- 초기 섭씨 온도 (숫자)
- 새로 설정할 온도 (숫자)
출력은 변경 전과 후의 온도 수치를 보여주는 네 줄이어야 합니다:
Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}예를 들어, 입력이 0과 100이라면 출력은 다음과 같아야 합니다:
Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212주의: 온도 값은 self가 아닌 :new() 내부의 지역 변수에 저장되어야 합니다. 게터와 세터 메서드는 클로저를 통해 이 프라이빗 변수에 접근할 수 있도록 생성자 내부에서 정의됩니다. temp.celsius 또는 temp._celsius에 직접 접근하려고 하면 nil이 반환되어야 합니다!
치트 시트
Lua 클래스에서 진정한 프라이빗 변수를 생성하려면, 클로저를 통해 지역 변수에 접근할 수 있는 생성자 내부에 getter와 setter 메서드를 정의하세요.
메서드는 클래스 테이블이 아닌 객체 인스턴스에 직접 할당되어, 각 인스턴스가 자체 프라이빗 변수에 접근할 수 있는 고유한 복사본을 갖게 됩니다:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- 프라이빗 변수
local obj = {}
-- 생성자 내부에 메서드 정의
function obj:getCount()
return count
end
function obj:increment()
count = count + 1
end
setmetatable(obj, Counter)
return obj
end사용법:
local c = Counter:new()
print(c:getCount()) -- 0
c:increment()
c:increment()
print(c:getCount()) -- 2
print(c.count) -- nil (여전히 숨겨져 있음!)트레이드오프: 각 인스턴스는 메서드의 자체 복사본을 갖게 되며(프로토타입을 통해 공유되지 않음), 이는 더 많은 메모리를 사용하지만 진정한 프라이버시를 제공합니다.
직접 해보기
-- Temperature 모듈을 불러옵니다
local Temperature = require('Temperature')
-- 입력 값을 읽습니다
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())
-- TODO: 초기 값으로 Temperature 인스턴스를 생성합니다
-- TODO: 초기 섭씨 및 화씨 온도를 출력합니다
-- TODO: setter를 사용하여 온도를 newTemp로 변경합니다
-- TODO: 업데이트된 섭씨 및 화씨 온도를 출력합니다
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.