Dieser Artikel setzt zumindest rudimentäre Lua Kenntnisse
vorraus. Komplexere Techniken, wie zum Beispiel Metatables werden
an den entsprechenden Stellen im Artikel kurz erklärt, Vorwissen
ist hier aber auch nicht schlecht. Ausführliche Informationen dazu
kann man im
Lua Manual und im
online Buch
Programming in Lua finden.
Wichtig sind Kenntnisse in Bezug auf Objekt Orientiertes
Programmieren (OOP). Dieses Konzept hier zu erläutern würde den
Rahmen des Artikels bei weitem sprengen. Bei
Wikipedia gibt es einen recht guten Artikel dazu.
Dieser Artikel bezieht sich allgemein auf Lua und ist nicht
nur für die Addon Programmierung für World of Warcraft gedacht.
Alle Beispiele lassen sich in einem Standalone Lua Interpreter der
Version 5.1 ausprobieren. Diesen Interpreter kann man unter
www.lua.org herunterladen, fertig
kompilierte Binaries gibt es auf der Seite
LuaBinaries.
Lua ist im Grunde genommen keine Objekt Orientierte Sprache, unterstützt das Konzept aber in gewisser Hinsicht. Einige Aspekte von OOP sind aber nur schwer zu verwirklichen.
1. self
Eine der Voraussetzungen für OOP ist, dass bei einem
Methodenaufruf die Methode Zugriff auf ihr Objekt hat.
Möglich wäre zum Beispiel folgendes:
-
function MeineKlasse.MeineMethode (objekt, irgendeinParameter, nocheinParameter )
-
print ( “MeineMethode aufgerufen” )
-
end
Ohne näher auf den Code einzugehen kann man schon sagen, dass
diese Art etwas unpraktisch ist. Man muss bei jedem Methodenaufruf
daran denken das richtige Objekt mit zu übergeben. Da Lua keine
Typprüfung kennt, kann es hier leicht zu komischen Fehlern kommen.
Es gibt aber eine bessere Art: Die “magische” Variable
self. Wenn eine Funktion, die in einer Table ist, auf eine
besondere Art definiert und aufgerufen wird, wird die umfassende
Table als unsichtbarer Parameter mit dem Namen “self” mitübergeben.
Das ganze sieht so aus:
-
MeineTable = { } – zuerst unsere Table MeineTable.variable = “Hallo” – eine Variable für die table function MeineTable:Test ( ) – wichtig ist hier der Doppelpunkt print (self.variable ) – hier ist der Zugriff auf das self end MeineTable:Test ( ) – und hier wird es aufgerufen
Beim Aufrufen von MeineTable:Test() mit dem Doppelpunkt wird unsichtbar MeineTable als erster Parameter übergeben. Das geht so weit, dass folgende Codestücke genau gleich sind:
-
Test = { } Test.name = “test” function Test:A ( ) – mit Doppelpunkt print (self.name ) end function Test.B (self ) – ohne Doppelpunkt print (self.name ) end – folgedes bewirkt alles das gleiche Test:A ( ) Test.A (Test ) Test:B ( ) Test.B (Test )
Dieses Lua Feature kann sehr gut für OOP benutzt werden.
2. Am Anfang war: Die Klasse
Jetzt wo wir wissen wie das
self funktioniert, kanns richtig losgehen.
Fangen wir also an, mit der Klasse Programmierer.
-
Programmierer = { } function Programmierer:TrinkKaffee ( ) print ( “Hm… lecker Kaffe!” ) end function Programmierer:TuWas ( ) print ( “*programmier*” ) end
Eine Klasse ist im Grunde genommen nichts anderes als eine
normale Table. In dieser Table befindet sich alles was zu unserer
Klasse gehört, im Moment also die zwei Methoden
TrinkKaffe und
TuWas. Damit kann der Programmierer also zwei wichtige
Dinge: Kaffee trinken und programmieren, mehr wird erstmal nicht
gebraucht.
Wir haben also eine praktisch vollwertige Programmierer
Klasse, können aber noch nichts damit anfangen. Uns fehlt ein
Objekt der Klasse, das gibts im 3. Kapitel.
3. Objekte
Um mit der Klasse etwas anzufangen brauchen wir ein Objekt der Klasse. Angenommen unser Programmierer heißt Klaus, sieht das ganze so aus:
-
Klaus = { } setmetatable (Klaus, { __index = Programmierer } ) Klaus:TrinkKaffee ( ) Klaus:TuWas ( )
Wie erwartet trinkt Klaus ersteinmal Kaffee (
Hm… lecker Kaffe!) und programmiert danach ein wenig (
*programmier*). Die Frage ist nur, was machen wir da
eigentlich. Nun, zunächst wird die globale Variable
Klaus, die unser Objekt darstellen soll, mit einer neuen,
leeren Table belegt. Danach kommt der “magische” Teil:
Die Variable
Klaus bekommt über die Funktion
setmetatable eine Metatable zugewiesen.
Eine Metatable ist im Grunde genommen nichts anderes als eine
normale Table, die besondere Informationen über eine Variable
enthält. Jede Variable in Lua kann eine Metatable haben. Diese
Metatable hat nur einen Eintrag Namens
__index der auf unsere Klasse
Programmierer zeigt. Der
__index Eintrag veranlasst Lua jedes Mal, wenn ein Element
von
Klaus angefordert wird, das nicht exisitert, in der
Programmierer-Table danach zu suchen.
Als Beispiel die nächste Zeile: Hier wird versucht auf die
Funktion
TrinkKaffee von
Klaus zuzugreifen. Da
Klaus selber diese Funktion nicht hat, wird in der Table
Programmierer nachgeschaut. Die Funktion existiert hier,
wird zurückgegeben und ausgeführt. Genau das gleiche passiert in
der Zeile danach.
Die Methoden der Klasse Programmierer existieren also nur in
dieser Klasse. Jedes Objekt der Klasse verweist nur auf die
Methoden, sodass bei jedem
:TrinkKaffee() die selbe Funktion
aufgerufen wird. Wenn wir ein Objekt namens Peter haben, dann wird
über
Peter:TrinkKaffee() genau das gleiche
TrinkKaffee wie bei dem vorherigen Beispiel aufgerufen. Der
Unterschied ist aber die unsichtbare
self Variable, welche jedes mal auf das Objekt verweist
Dieser Vorgang ist der zentrale Punkt beim OOP in Lua.
Da das ganze Objekt erstellen jedes Mal ausgeführt werden muss, wenn wir ein neues Objekt der Klasse haben möchten, packen wir es am besten in eine eigene Funktion:
-
function Programmierer:New ( ) local object = { } setmetatable (object, { __index = self } ) return object end
Jetzt können wir einfach
Klaus = Programmierer:New() schreiben,
und haben einen neuen Programmierer, der Kaffee trinken und
programmieren kann. Außerdem können wir in
New später benötigte Variablen initialisieren.
Variablen die zu einem Objekt gehören (also nicht statische
Variablen) müssen immer Teil von
self, also der Objekt Table sein. Statische Variablen sind
Teil der Klassentable und damit in allen Objekten verfügbar. Hier
mal ein Beispiel:
-
Programmierer = { } Programmierer.anzahl = 0 Programmierer:New ( ) local obj = setmetatable ( { }, { __index = self } ) <br /> – Kurzform von obj = {}; setmetatable(obj, …) obj.kaffeeGetrunken = false Programmierer.anzahl = Programmierer.anzahl + 1 end Programmierer:TrinkKaffe() print(“Lecker Kaffee!”) self.kaffeeGetrunken = true end Programmierer:TuWas() if self.kaffeeGetrunken then print(“Einer von ” .. Programmierer.anzahl .. ” Programmierern geht an die Arbeit.”) else print(“Erstmal Kaffee trinken!”) end end Klaus = Programmierer:New() Peter = Programmierer:New() Klaus:TrinkKaffee() – Lecker Kaffee! Klaus:TuWas() – Einer von 2 Programmierern geht an die Arbeit Peter:TuWas() – Erstmal Kaffee trinken!
Die Variable Programmierer.anzahl ist in allen Objekten der Klasse bekannt, und geht alle Objekte etwas an. kaffeGetrunken hingegen ist Teil des einzelnen Objektes, und hat nichts mit den anderen Objekten zu tun.
Der Funktion New könnte man auch Parameter übergeben. Wenn der Programmierer zum Beispiel einen Namen haben soll, geht folgendes:
-
function Programmierer:New (name ) local obj = setmetatable ( { }, { __index = self } ) obj.name = name return obj end function Programmierer:Hallo ( ) print ( “Hallo, ich bin “ .. self.name ) end Klaus = Programmierer:New ( “Klaus” ) Klaus:Hallo ( ) – schreibt “Hallo ich bin Klaus”.
hier geht es zur zweiten Seite
Seiten: 1 2









