0Pricing
Lua Academy · บทเรียน

การห่อหุ้มด้วยคลอเชอร์

ซ่อนสถานะส่วนตัวด้วยรูปแบบออบเจกต์ที่อาศัยคลอเชอร์

การห่อหุ้มด้วยคลอเชอร์ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. คลาสผ่านเมทาเทเบิล
  2. ตัวสร้างและ new()
  3. เมธอดของอินสแตนซ์และ self
  4. การห่อหุ้มด้วยคลอเชอร์
← กลับไปที่ Lua Academy