การห่อหุ้มด้วยคลอเชอร์
ซ่อนสถานะส่วนตัวด้วยรูปแบบออบเจกต์ที่อาศัยคลอเชอร์
การห่อหุ้มด้วยคลอเชอร์ เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน
วัตถุที่อาศัยคลอเชอร์
อีกทางเลือกหนึ่งนอกเหนือจาก OOP ด้วยเมตาเทเบิลคือใช้คลอเชอร์เพื่อซ่อนสถานะ วัตถุเป็นตารางฟังก์ชัน และฟังก์ชันทั้งหมดจะปิดล้อมตัวแปรส่วนตัวไว้ ไม่ต้องใช้เมตาเทเบิล ทำให้สถานะเป็นส่วนตัวอย่างแท้จริงและไม่สามารถเข้าถึงจากภายนอกได้
local function makeCounter(start)
local count = start or 0 -- truly private
return {
increment = function() count=count+1 end,
decrement = function() count=count-1 end,
get = function() return count end,
reset = function() count=0 end,
}
end
local c = makeCounter(10)
c.increment(); c.increment()
print(c.get()) -- 12
print(c.count) -- nil (truly private!)ส่วนตัวกับสาธารณะ
สำหรับคลอเชอร์ ความแตกต่างระหว่างส่วนตัวกับสาธารณะมีอยู่อย่างชัดเจน คลอเชอร์ใช้ค่าอัปแวลูร่วมกัน (ส่วนตัว) ส่วนโค้ดภายนอกจะเห็นเฉพาะตารางที่ส่งคืน (สาธารณะ) ไม่มีทางเข้าถึงตัวแปรส่วนตัวได้โดยไม่ผ่าน API สาธารณะ
local function makeAccount(initial)
local balance = initial
local transactions = {}
local function record(type, amount)
transactions[#transactions+1] = {type=type,amount=amount}
end
return {
deposit = function(n) record("D",n); balance=balance+n end,
withdraw = function(n)
if n>balance then error("insufficient funds") end
record("W",n); balance=balance-n
end,
balance = function() return balance end,
history = function() return transactions end,
}
endคลอเชอร์ที่ใช้ร่วมกัน
ฟังก์ชันหลายรายการในตารางที่ส่งคืนเดียวกันจะใช้ค่าอัปแวลูร่วมกัน วิธีนี้ทำให้เมธอดต่าง ๆ ใช้สถานะร่วมกันได้โดยไม่เปิดเผยต่อสาธารณะ เมธอดทั้งหมดจะเห็นตัวแปรส่วนตัวชุดเดียวกัน
local function makeQueue()
local items = {} -- shared by all methods
local size = 0
return {
enqueue = function(v) size=size+1; items[size]=v end,
dequeue = function()
if size==0 then return nil end
local head=1
while items[head]==nil do head=head+1 end
local v=items[head]; items[head]=nil; return v
end,
isEmpty = function() return size==0 end,
count = function() return size end,
}
endคลอเชอร์กับเมตาเทเบิล
คลอเชอร์: สถานะเป็นส่วนตัวอย่างแท้จริง ไม่มีค่าใช้จ่ายจากเมตาเทเบิล และแต่ละอินสแตนซ์มีสำเนาฟังก์ชันของตนเอง (ใช้หน่วยความจำมากกว่า) เมตาเทเบิล: ใช้เมธอดร่วมกัน (ใช้หน่วยความจำน้อยกว่า) ตรวจสอบได้ด้วยเครื่องมือดีบัก และรองรับการสืบทอด ควรเลือกตามความต้องการด้านความเป็นส่วนตัวและจำนวนอินสแตนซ์
-- Closure: per-instance functions (more memory)
local function makePoint(x,y)
return {
x = function() return x end,
y = function() return y end,
distance = function()
return math.sqrt(x*x+y*y)
end
}
end
-- Metatable: shared methods (less memory)
-- local Point = {}; Point.__index = Point
-- function Point.new(x,y) return setmetatable({x=x,y=y},Point) endคลอเชอร์ที่เปลี่ยนแปลงได้
การสร้างคลอเชอร์ที่ครอบตัวแปรซึ่งเปลี่ยนแปลงได้ ช่วยให้คุณสร้างวัตถุที่มีสถานะได้โดยไม่ต้องใช้ตารางเลย คลอเชอร์เดียวสามารถเก็บสถานะและคำนวณผลลัพธ์ได้
local function makeAccumulator()
local sum, count = 0, 0
return {
add = function(n) sum=sum+n; count=count+1 end,
mean = function()
return count>0 and sum/count or 0
end,
total = function() return sum end,
n = function() return count end,
}
end
local acc = makeAccumulator()
for _, v in ipairs({10,20,30,40,50}) do
acc.add(v)
end
print(acc.total(),acc.mean(),acc.n()) -- 150 30.0 5ตัวสร้างพร้อมการตรวจสอบ
ตัวสร้างที่ใช้คลอเชอร์จะตรวจสอบค่าในฟังก์ชันโรงงาน ไม่ใช่ในเมธอด หากอินพุตไม่ถูกต้อง โรงงานจะส่งคืน nil หรือข้อผิดพลาดก่อนสร้างวัตถุใด ๆ
local function makeRange(lo, hi)
assert(lo<=hi,"lo must be <= hi")
return {
contains = function(v) return v>=lo and v<=hi end,
clamp = function(v) return math.max(lo,math.min(hi,v)) end,
lo = function() return lo end,
hi = function() return hi end,
span = function() return hi-lo end,
}
end
local r = makeRange(0,100)
print(r.contains(50)) -- true
print(r.clamp(150)) -- 100เหตุการณ์ด้วยคลอเชอร์
ตัวส่งเหตุการณ์ที่สร้างด้วยคลอเชอร์จะซ่อนรายการตัวจัดการไว้เป็นส่วนตัว ส่วน add/remove/emit คือ API สาธารณะ
local function makeEmitter()
local handlers = {}
return {
on = function(event, fn)
handlers[event] = handlers[event] or {}
handlers[event][#handlers[event]+1] = fn
end,
emit = function(event, ...)
for _, fn in ipairs(handlers[event] or {}) do
fn(...)
end
end,
off = function(event) handlers[event] = {} end,
}
end
local emitter = makeEmitter()
emitter.on("data", function(v) print("Got:", v) end)
emitter.emit("data", 42) -- Got: 42การจดจำผลลัพธ์ด้วยคลอเชอร์
ตัวห่อสำหรับจดจำผลลัพธ์ที่ใช้คลอเชอร์จะเก็บแคชและฟังก์ชันที่ถูกห่อไว้เป็นส่วนตัว และส่งคืนเฉพาะเวอร์ชันที่จดจำผลลัพธ์แล้ว
local function memoize(fn)
local cache = {}
return function(...)
local key = table.concat({...}, ",")
if cache[key] == nil then
cache[key] = fn(...)
end
return cache[key]
end
end
local expensiveFib
expensiveFib = memoize(function(n)
if n<=1 then return n end
return expensiveFib(n-1)+expensiveFib(n-2)
end)
for i=0,10 do io.write(expensiveFib(i).." ") end
print()การขยายวัตถุที่อาศัยคลอเชอร์
คุณสามารถขยายวัตถุที่อาศัยคลอเชอร์ได้โดยสร้างวัตถุใหม่ที่เรียกเมธอดของวัตถุเดิม ลักษณะนี้เหมือนการห่อ ไม่ใช่การสืบทอด เพราะมีการมอบหมายงานไปยังวัตถุเดิม
local function makeTimedCounter(start)
local base = makeCounter and makeCounter(start) or (function()
local c=start or 0
return {inc=function()c=c+1 end,get=function()return c end}
end)()
local callCount = 0
return {
increment = function()
callCount=callCount+1
base.inc()
end,
get = base.get,
calls = function() return callCount end,
}
endควรใช้คลอเชอร์เมื่อใด
ใช้ OOP ที่อาศัยคลอเชอร์เมื่อคุณต้องการสถานะส่วนตัวอย่างแท้จริงที่ต้องปลอดภัย มีอินสแตนซ์จำนวนน้อย (หน่วยความจำไม่ใช่ข้อกังวล) หรือกำลังเขียนโค้ดรูปแบบฟังก์ชัน ใช้เมตาเทเบิลเมื่อคุณต้องการการสืบทอด มีอินสแตนซ์จำนวนมาก หรือต้องการผสานรวมกับไลบรารี OOP
-- Guideline summary:
-- Closures: true privacy, no metatable, self-contained
-- Metatables: shared methods, inheritance, instanceof checks
-- Rule of thumb:
-- < 100 instances with private state -> closures
-- > 100 instances OR need inheritance -> metatables
print("Both patterns are valid Lua OOP.")ตรวจสอบความเข้าใจอย่างรวดเร็ว
ข้อได้เปรียบหลักของวัตถุที่อาศัยคลอเชอร์เหนือวัตถุที่อาศัยเมตาเทเบิลคืออะไร
สรุป: คลอเชอร์สำหรับการห่อหุ้ม
สรุป:
- รูปแบบคลอเชอร์: ฟังก์ชันโรงงานส่งคืนตารางเมธอด ส่วนสถานะเป็นอัปแวลู
- เป็นส่วนตัวอย่างแท้จริง: ไม่มีทางเข้าถึงอัปแวลูจากภายนอก
- เมธอดทั้งหมดที่ส่งคืนจะใช้อัปแวลูส่วนตัวร่วมกัน
- ข้อแลกเปลี่ยน: ใช้หน่วยความจำต่ออินสแตนซ์มากขึ้น แต่มีความเป็นส่วนตัวดีกว่า
- เหมาะกับโค้ดที่ต้องการความปลอดภัยสูงหรือการออกแบบรูปแบบฟังก์ชัน
คำถามที่พบบ่อย
บทเรียน “การห่อหุ้มด้วยคลอเชอร์” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การห่อหุ้มด้วยคลอเชอร์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การห่อหุ้มด้วยคลอเชอร์”
ซ่อนสถานะส่วนตัวด้วยรูปแบบออบเจกต์ที่อาศัยคลอเชอร์ คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การห่อหุ้มด้วยคลอเชอร์” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม
ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- คลาสผ่านเมทาเทเบิล
- ตัวสร้างและ new()
- เมธอดของอินสแตนซ์และ self
- การห่อหุ้มด้วยคลอเชอร์