Lua Academy · Lektion

Kapselung mit Closures

Verbergen Sie privaten Zustand mit objektorientierten Mustern auf Basis von Closures

Lektion 4 von 412 Schritte

Kapselung mit Closures ist eine kostenlose Lua Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Lua Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Lua Academy-Kurs umfasst insgesamt 4 Lektionen.

Closure-basierte Objekte

Eine Alternative zur objektorientierten Programmierung mit Metatabellen: Verwenden Sie eine Closure, um den Zustand zu verbergen. Das Objekt ist eine Tabelle aus Funktionen; alle Funktionen schließen die privaten Variablen ein. Es ist keine Metatabelle erforderlich. Der private Zustand ist tatsächlich privat und von außen unerreichbar.

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

Privat und öffentlich

Bei Closures ist der Unterschied zwischen privat und öffentlich absolut: Closures teilen sich den Upvalue (privat), während externer Code nur die zurückgegebene Tabelle sieht (öffentlich). Es gibt keine Möglichkeit, auf private Variablen zuzugreifen, ohne die öffentliche API zu verwenden.

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

Gemeinsam verwendete Closures

Mehrere Funktionen in derselben zurückgegebenen Tabelle teilen sich dieselben Upvalues. So wird Zustand zwischen Methoden geteilt, ohne ihn öffentlich zugänglich zu machen. Alle Methoden sehen dieselben privaten Variablen.

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

Closures und Metatabellen

Closures: tatsächlich privater Zustand, kein Overhead durch Metatabellen, aber jede Instanz besitzt eigene Funktionskopien (mehr Speicher). Metatabellen: gemeinsam genutzte Methoden (weniger Speicher), mit Debug-Werkzeugen inspizierbar und Unterstützung für Vererbung. Treffen Sie die Wahl abhängig von den Anforderungen an Datenschutz und der Anzahl der Instanzen.

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

Veränderliche Closures

Closures über veränderlichen Variablen ermöglichen es, zustandsbehaftete Objekte ganz ohne Tabellen zu erstellen. Eine einzelne Closure kann Zustand speichern und Ergebnisse berechnen.

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

Konstruktor mit Validierung

Konstruktoren auf Closure-Basis validieren in der Factory-Funktion und nicht in einer Methode. Ungültige Eingaben führen dazu, dass die Factory nil oder einen Fehler zurückgibt, bevor ein Objekt erstellt wird.

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

Ereignisse mit Closures

Ein mit Closures implementierter Event Emitter: Die Liste der Handler ist privat; add/remove/emit bilden die öffentliche 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

Memoization mit Closures

Ein Memoize-Wrapper mithilfe von Closures: Der Cache und die umschlossene Funktion sind privat; zurückgegeben wird nur die memoizierte Version.

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

Closure-Objekte erweitern

Sie können ein Closure-basiertes Objekt erweitern, indem Sie ein neues Objekt erstellen, das die Methoden des ursprünglichen Objekts aufruft. Das ähnelt Wrapping, nicht Vererbung — an das ursprüngliche Objekt wird delegiert.

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

Wann Sie Closures verwenden sollten

Verwenden Sie Closure-basierte objektorientierte Programmierung, wenn Sie tatsächlich privaten und geschützten Zustand benötigen, nur wenige Instanzen haben (Speicher keine große Rolle spielt) oder Code im funktionalen Stil schreiben. Verwenden Sie Metatabellen, wenn Sie Vererbung oder viele Instanzen benötigen oder OOP-Bibliotheken integrieren möchten.

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

Kurztest

Was ist der wichtigste Vorteil von Closure-basierten Objekten gegenüber Objekten auf Metatabellenbasis?

Zusammenfassung: Kapselung mit Closures

Zusammenfassung:

  • Closure-Muster: Eine Factory-Funktion gibt eine Methodentabelle zurück; der Zustand ist ein Upvalue
  • Tatsächlich privat: Kein Zugriff auf Upvalues von außen möglich
  • Alle zurückgegebenen Methoden teilen sich dieselben privaten Upvalues
  • Kompromiss: mehr Speicher pro Instanz gegenüber besserem Schutz der Privatsphäre
  • Verwenden Sie dies für sicherheitskritischen Code oder funktionale Entwürfe
Kostenlos starten

Lerne Lua mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
40
Lektionen
159

Häufig gestellte Fragen

Ist die Lektion „Kapselung mit Closures“ kostenlos?

Ja — der vollständige Text von „Kapselung mit Closures“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Lua Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Lua Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Kapselung mit Closures“?

Verbergen Sie privaten Zustand mit objektorientierten Mustern auf Basis von Closures Du übst Lua Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Lua Academy zu starten?

Keine Vorkenntnisse erforderlich. Lua Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Kapselung mit Closures“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Lua Academy-Lektion Code schreiben und ausführen?

Ja. Jede Lua Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Klassen über Metatabellen
  2. Konstruktoren und new()
  3. Instanzmethoden und self
  4. Kapselung mit Closures
← Zurück zu Lua Academy