Objekt Orientiertes Programmieren in Lua

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:


  1. function MeineKlasse.MeineMethode (objekt, irgendeinParameter, nocheinParameter )
  2. print ( “MeineMethode aufgerufen” )
  3. 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:


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


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


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


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


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


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


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

MoP Vorbestellung
Umfrage

Offenbar wird es leerer auf den WoW Realms. Ist das auch für euch spürbar?

Ergebnis anzeigen »

Loading ... Loading ...
Klassenguides
ContentAd
Patch 4.3 Vorschau
SW:TOR Teaser
 
...dass es in Catacysm auch wieder einen alten Gott zu sehen geben wird? Um wen es sich dabei wohl handelt. Gerüchte besagen, dass sogar Thrall ein alter Gott sein könnte - ohne es selber zu wissen.
Rectangle
mySigs
mySigs.de
Shakes Fidget
Facebook
Interessantes und Ungewöhnliches rund um World of Warcraft