Lua Academy · บทเรียน

รูปแบบมอดูลและแนวทางปฏิบัติที่ดี

ใช้รูปแบบ local M = {} และเปิดเผย API สาธารณะอย่างเป็นระเบียบ

บทเรียน 4 จาก 412 ขั้นตอน

รูปแบบมอดูลและแนวทางปฏิบัติที่ดี เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

รูปแบบ M = {}

รูปแบบโมดูล Lua ที่ใช้ได้ทั่วไปคือ ประกาศตาราง local เติมข้อมูลลงในตาราง แล้วส่งคืนตารางนั้น สัญลักษณ์สาธารณะทั้งหมดจะอยู่ใน M ส่วนตัวช่วยส่วนตัวทั้งหมดจะเป็นตัวแปร local ธรรมดา รูปแบบนี้ชัดเจน เรียบง่าย และใช้งานได้ทุกที่

-- The canonical module pattern
local M = {}

-- Private helper (not exported)
local function validate(x)
  return type(x) == "number" and x >= 0
end

-- Public API
function M.sqrt(x)
  assert(validate(x), "expected non-negative number")
  return math.sqrt(x)
end

M.PI = math.pi

return M

โมดูลที่อ้างอิงตัวเอง

ภายในโมดูล ฟังก์ชันสามารถเรียกใช้ฟังก์ชันอื่นของโมดูลได้ทั้งโดยใช้ชื่อ (M.foo()) หรือใช้ตัวแปร local การใช้ตัวแปร local จะเร็วกว่าเล็กน้อย ส่วนการใช้ M.foo() ทำให้ผู้ใช้แทนที่ M.foo ได้ และการเรียกใช้ภายในจะใช้ฟังก์ชันที่แทนที่นั้นด้วย (การแก้ไขแพตช์ขณะทำงาน)

local M = {}

-- Option A: use M.foo inside (allows override)
function M.double(n) return M.multiply(n, 2) end
function M.multiply(a, b) return a * b end

-- Option B: use local function (faster, no override)
local function mul(a, b) return a * b end
function M.triple(n) return mul(n, 3) end

return M

รูปแบบ Singleton

โมดูลสามารถทำหน้าที่เป็น singleton ได้ โดยมีสถานะภายในที่เปลี่ยนแปลงได้และใช้ร่วมกันโดยผู้เรียกใช้ทั้งหมด เนื่องจาก require เก็บโมดูลไว้ในแคช การเรียกใช้ require("mod") ทุกครั้งจึงได้รับออบเจ็กต์เดียวกันที่มีสถานะเดียวกัน

-- config.lua (singleton)
local M = {}
local _config = {env="dev", logLevel="info"}

function M.set(key, val)
  _config[key] = val
end

function M.get(key)
  return _config[key]
end

function M.load(t)
  for k,v in pairs(t) do _config[k]=v end
end

return M

-- All callers share the same config:

โมดูลในฐานะเนมสเปซ

ใช้โมดูลเป็นเนมสเปซโดยเฉพาะ เพื่อหลีกเลี่ยงการทำให้ตารางโกลบอลเต็มไปด้วยข้อมูล จัดกลุ่มค่าคงที่และเครื่องมือที่เกี่ยวข้องไว้ภายใต้ชื่อเดียว เช่นเดียวกับแพ็กเกจในภาษาอื่น

-- constants.lua
local M = {
  HTTP = {
    OK=200, CREATED=201, NO_CONTENT=204,
    BAD_REQUEST=400, UNAUTHORIZED=401,
    NOT_FOUND=404, SERVER_ERROR=500,
  },
  COLORS = {RED="#FF0000", GREEN="#00FF00", BLUE="#0000FF"},
  MAX_RETRIES = 3,
  TIMEOUT_SEC = 30,
}
return M

-- local C = require("constants")
-- if status == C.HTTP.NOT_FOUND then ...

โมดูลแบบ Factory

โมดูลที่ส่งออกฟังก์ชัน factory แทนที่จะส่งออกตารางธรรมดา factory จะสร้างและส่งคืนอินสแตนซ์ใหม่ที่มีสถานะส่วนตัวของตัวเอง นี่คือรูปแบบคลาสในระดับโมดูล

-- logger.lua
local M = {}

function M.new(name, level)
  level = level or "info"
  local levels = {debug=1,info=2,warn=3,error=4}
  local self = {}
  
  function self.log(msgLevel, msg)
    if levels[msgLevel] >= levels[level] then
      print(string.format("[%s][%s] %s", name, msgLevel:upper(), msg))
    end
  end
  
  function self.info(msg)  self.log("info",  msg) end
  function self.warn(msg)  self.log("warn",  msg) end
  function self.error(msg) self.log("error", msg) end
  
  return self
end

return M

ฟังก์ชันเริ่มต้นโมดูล

โมดูลบางชนิดจำเป็นต้องมีการตั้งค่าก่อนใช้งาน ให้ฟังก์ชัน M.init(config) ทำหน้าที่เก็บการตั้งค่าไว้ในสถานะส่วนตัวของโมดูล วิธีนี้ช่วยให้ฉีดการพึ่งพาและทดสอบได้ง่ายขึ้น

-- db.lua
local M = {}
local pool = nil

function M.init(config)
  pool = {
    host = config.host or "localhost",
    port = config.port or 5432,
    connections = {},
  }
  print("DB initialized:", pool.host, pool.port)
end

function M.query(sql)
  assert(pool, "call db.init() first")
  -- ... execute query
  return {}
end

return M

โมดูลที่เปลี่ยนแปลงไม่ได้

ป้องกันไม่ให้ผู้ใช้แก้ไข API ของโมดูลโดยไม่ตั้งใจ โดยใช้ __newindex เพื่อบล็อกการเขียนทั้งหมด วิธีนี้มีประโยชน์อย่างยิ่งสำหรับโมดูลไลบรารี ซึ่งการแก้ไขแพตช์ขณะทำงานโดยไม่ตั้งใจอาจทำให้การทำงานเสียหาย

local function freeze(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function(_, k, _)
      error("module is read-only, cannot set: " .. tostring(k), 2)
    end
  })
end

local M = {}
function M.add(a, b) return a + b end
function M.sub(a, b) return a - b end

return freeze(M)

การจัดทำเอกสารด้วย LDoc

ธรรมเนียมทั่วไปในการจัดทำเอกสารโมดูล Lua คือการใช้ความคิดเห็นรูปแบบ LDoc ที่มีคำนำหน้า --- แม้ภาษาเองจะไม่ได้บังคับ แต่ความคิดเห็นเหล่านี้ช่วยให้เครื่องมือสร้างเอกสารสร้างเอกสาร API โดยอัตโนมัติ

--- A utility module for string operations.
-- @module stringutils
local M = {}

--- Trim leading and trailing whitespace.
-- @param s string The input string.
-- @return string The trimmed string.
function M.trim(s)
  return s:match("^%s*(.-)%s*$")
end

--- Count occurrences of a substring.
-- @param str string The string to search.
-- @param sub string The substring to count.
-- @return number Count of occurrences.
function M.count(str, sub)
  local _, n = str:gsub(sub, "")
  return n
end

return M

การทดสอบโมดูล

ทดสอบโมดูลโดยเรียกใช้ผ่าน require แล้วทดลองใช้แต่ละฟังก์ชัน ใช้ตัวเรียกการทดสอบอย่างง่ายหรือ busted (กรอบงานการทดสอบของ Lua) เก็บการทดสอบไว้ในไฟล์แยกที่มีเส้นทางสอดคล้องกับโมดูล

-- test/test_stringutils.lua
local su = require("stringutils")

local function test(name, fn)
  local ok, err = pcall(fn)
  if ok then print("[PASS] " .. name)
  else   print("[FAIL] " .. name .. ": " .. err)
  end
end

test("trim removes spaces", function()
  assert(su.trim("  hello  ") == "hello")
end)

test("trim empty string", function()
  assert(su.trim("") == "")
end)

test("count occurrences", function()
  assert(su.count("banana", "a") == 3)
end)

การประกอบโมดูล

ระบบที่ซับซ้อนประกอบขึ้นจากหลายโมดูล จุดเริ่มต้นหลักจะเรียกใช้และเชื่อมต่อโมดูลย่อยเข้าด้วยกัน การแยกความรับผิดชอบเช่นนี้ทำให้แต่ละโมดูลมุ่งเน้นงานของตนเองและสามารถทดสอบแยกกันได้

-- app.lua (main entry point)
local config = require("config")
local db     = require("db")
local server = require("server")

-- Configure from environment
config.load({
  dbHost = os.getenv("DB_HOST") or "localhost",
  port   = tonumber(os.getenv("PORT")) or 8080,
})

-- Wire modules together
db.init({host=config.get("dbHost"), port=5432})
server.init({port=config.get("port"), db=db})
server.start()

ตรวจสอบความเข้าใจอย่างรวดเร็ว

จุดประสงค์หลักของรูปแบบโมดูล local M = {} ... return M คืออะไร

สรุป: แนวทางปฏิบัติที่ดีในการสร้างโมดูล

สรุป:

  • ใช้ local M = {} ... return M เสมอ
  • ข้อมูลส่วนตัว = ตัวแปร local ระดับไฟล์ ข้อมูลสาธารณะ = ฟิลด์ของ M
  • Singleton: require เก็บอินสแตนซ์โมดูลไว้ในแคช
  • ใช้ฟังก์ชัน factory สำหรับสถานะที่แยกกันในแต่ละอินสแตนซ์
  • ทำให้โมดูลไม่เปลี่ยนแปลงด้วย __newindex เพื่อป้องกันการแก้ไข
  • ทดสอบในไฟล์แยก และจัดทำเอกสารด้วยความคิดเห็น ---
เริ่มต้นได้ฟรี

เรียนรู้ Lua ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
40
บทเรียน
159

คำถามที่พบบ่อย

บทเรียน “รูปแบบมอดูลและแนวทางปฏิบัติที่ดี” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รูปแบบมอดูลและแนวทางปฏิบัติที่ดี” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบมอดูลและแนวทางปฏิบัติที่ดี”

ใช้รูปแบบ local M = {} และเปิดเผย API สาธารณะอย่างเป็นระเบียบ คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “รูปแบบมอดูลและแนวทางปฏิบัติที่ดี” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม

ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. ฟังก์ชัน require
  2. การเขียนไฟล์มอดูล
  3. package.path และ package.cpath
  4. รูปแบบมอดูลและแนวทางปฏิบัติที่ดี
← กลับไปที่ Lua Academy