0Pricing
Lua Academy · レッスン

モジュールのパターンとベストプラクティス

local M = {}パターンを使い、公開APIを明確に提供します。

「モジュールのパターンとベストプラクティス」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

M = {} パターン

Lua で広く使われるモジュールパターンは、ローカルテーブルを宣言し、内容を設定して返す方法です。公開するシンボルはすべて M に入れ、非公開のヘルパーは通常のローカル変数にします。明確で最小限の構成であり、どの環境でも機能します。

-- 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())呼び出すことも、ローカル変数として呼び出すこともできます。ローカル変数を使うほうがやや高速です。一方、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

シングルトンパターン

モジュールをシングルトンとして機能させることができます。モジュールは、すべての呼び出し側で共有される変更可能な内部状態を持ちます。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:

名前空間としてのモジュール

グローバルテーブルを汚染しないように、モジュールを名前空間としてのみ使用します。他の言語のパッケージのように、関連する定数やユーティリティを 1 つの名前の下にまとめます。

-- 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 ...

ファクトリーモジュール

単純なテーブルではなく、ファクトリ関数をエクスポートするモジュールです。ファクトリーは、それぞれが独自の非公開状態を持つ新しいインスタンスを作成して返します。これはモジュールレベルで実現するクラスパターンです。

-- 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

モジュールの Init 関数

使用前に設定が必要なモジュールもあります。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

不変モジュール

__newindex を使ってすべての書き込みをブロックし、ユーザーが誤ってモジュール API を変更するのを防ぎます。誤ったモンキーパッチによって問題が発生する可能性があるライブラリモジュールで特に便利です。

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)

モジュールの組み合わせ

複雑なシステムでは複数のモジュールを組み合わせます。メインのエントリーポイントでサブモジュールを require し、それらを連携させます。この関心の分離により、各モジュールの役割が明確になり、独立してテストできるようになります。

-- 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 を使います
  • 非公開=ファイルレベルのローカル変数、公開=M のフィールド
  • シングルトンでは、require がモジュールインスタンスをキャッシュします
  • インスタンスごとの状態にはファクトリ関数を使います
  • __newindex でモジュールを凍結し、変更を防ぎます
  • 別ファイルでテストし、--- コメントでドキュメントを作成します

よくある質問

「モジュールのパターンとベストプラクティス」レッスンは無料ですか?

はい。「モジュールのパターンとベストプラクティス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「モジュールのパターンとベストプラクティス」で何を学びますか?

local M = {}パターンを使い、公開APIを明確に提供します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「モジュールのパターンとベストプラクティス」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. require関数
  2. モジュールファイルの作成
  3. package.pathとpackage.cpath
  4. モジュールのパターンとベストプラクティス
← Lua Academyに戻る