4. Vererbung
Wichtig für OOP ist die Vererbung. Mit Metatables lässt sich das auch in Lua ermöglichen. Um Beispielsweise die Klasse Programmierer von der Klasse Mensch abzuleiten, kann man folgendermaßen vorgehen:
-
Mensch = { } function Mensch:Schlafen ( ) print ( “Zzzz” ) end function Mensch:Arbeiten ( ) print ( “Lieber nicht…” ) end function Mensch:New (name ) local obj = setmetatable ( { }, { __index = self } ) obj.name = name return obj end Programmierer = { } setmetatable (Programmierer, { __index = Mensch } ) – Das ist der wichtige Punkt function Programmierer:TrinkKaffee ( ) print ( “Hm… lecker Kaffe!” ) self.kaffee = true end function Programmierer:New ( ) local obj = setmetatable ( { }, { __index = self } ) return obj end Spieler = { } setmetatable (Spieler, { __index = Mensch } ) – Hier auch function Spieler:WoWSpielen ( ) end Klaus = Programmierer:New ( “Klaus” ) Klaus:TrinkKaffe ( ) Klaus:Schlafen ( ) Klaus:Arbeiten ( ) Peter = Spieler:New ( “Peter” ) Peter:Schlafen ( ) Peter:Arbeiten ( ) Peter:WoWSpielen ( )
Sowohl Programmierer als auch die neue Klasse Spieler sind von
der Klasse Mensch abgeleitet. Damit können sie alles tun, was ein
Mensch (im Sinne der Klasse) tun kann.
Funktionieren tut das wieder über eine Metatable mit
__index. Diesmal wird die Metatable aber für die Klasse und
nicht für das fertige Objekt benutzt, so dass ein misslungener
Zugriff auf die Klasse direkt auf die übergeordnete umgeleitet
wird.
Mehrfachvererbung ist mit ein wenig trickserei auch möglich:
-
ElternKlasseA = { } function ElternKlasseA:HalloA ( ) print ( “A” ) end ElternKlasseB = { } function ElternKlasseB:HalloB ( ) print ( “B” ) end KindKlasse = { } do – hier kommt die Vererbung local parents = { } for k, v in pairs (ElternKlasseA ) do parents = v end for k, v in pairs (ElternKlasseB ) do parents = v end setmetatable (KindKlasse, { __index = parents } ) end function KindKlasse:New ( ) return setmetatable ( { }, { __index = self } ) end – alle möglichen Sachen für die Kindklasse objekt = KindKlasse:New ( ) objekt:HalloA ( ) objekt:HalloB ( )
In dem do… end Block werden alle Elemente der beiden Elternklassen in einer Table zusammengefasst, sodass über das __index auf beide zugegriffen werden kann. Das ganze in do… end zusammenzufassen hat den Vorteil, dass es zu keinem Namenskonflikt mit der Variablen parents kommen kann.
5. Zusätzliches
Jetzt kommen noch drei Sachen, um das ganze etwas zu verbessern.
Als erstes ein schönerer Weg um Objekte zu erstellen:
-
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 setmetatable (Programmierer, { __call = Programmierer.New } ) Klaus = Programmierer ( “Klaus” ) Klaus:Hallo ( )
Durch das __call in der Metatable kann Programmierer wie eine Funktion bentutzt werden; jedes mal wenn es aufgerufen wird, wird Programmierer.New aufgerufen.
Das nächste sind private Daten. Das sind Daten die nur von einem
Objekt der Klasse erreichbar sind. Dadurch kann sichergestellt
werden, dass diese Daten nicht “von außen” verändert werden, oder
Funktionen des Objekts aufgerufen werden, die nur für die interne
Nutzung vorgesehen sind.
Das ganze wird über lokale Variablen in einem eigenen Block
realisiert:
-
Programmierer = { } do – alle lokalen Variablen zwischen dem do und dem end sind nur hier verfügbar local privat = { } local function TuWirklichWas (self ) print (self.name .. ” programmiert.” ) end function Programmierer:New (name ) local obj = setmetatable ( { }, { __index = self } ) privat = { } – hier wird ein Platz für die privaten Daten des Objekts angelegt obj.name = name privat.kaffeeGetrunken = false return obj end function Programmierer:TrinkKaffee ( ) local daten = privat daten.kaffeGetrunken = true print ( “Lecker Kaffee!” ) end function Programmierer:TuWas ( ) local daten = privat if daten.kaffeeGetrunken then TuWirklichWas (self ) else print ( “Erstmal Kaffee trinken…” ) end end end
Die privaten Daten eines Objekts werden in einer Table
gespeichert. Diese Table steckt wiederum in der Table
privat, die nur innerhalb des
do…
end Blocks zugänglich ist. über den Trick, das Objekt als
Index in dieser Table zu verwenden, kommt man später sehr leicht
wieder an die eigentlichen Daten.
Die privaten Funktionen werden direkt als
local function deklariert. Hier ist es
wichtig, dass man das eigentliche Objekt an die Funktion übergibt,
damit sie was damit machen kann.
Damit ist jetzt sichergestellt, dass der Programmierer auch
immer genug Kaffee bekommt, bevor er arbeiten muss.
Zu guter letzt wird die Ausgabe von Objekten verschönert. Wenn
man versucht ein Objekt auszugeben bekommt man nur etwas wie
table: 0035B7F8 statt einer schönen Ausgabe.
Aber auch da hilft wieder die Metatable:
-
function Programmierer:ToString ( ) print ( “Programmierer (“..self.name.. “)” ) end function Programmierer:New (name ) local obj = setmetatable ( { }, { __index = self, __tostring = self.ToString } ) obj.name = name return obj end function Programmierer:Hallo ( ) print ( “Hallo, ich bin “ .. self.name ) end setmetatable (Programmierer, { __call = Programmierer.New } ) Klaus = Programmierer ( “Klaus” ) print (Klaus ) – schreibt “Programmierer (Klaus)”
Und schon ist auch die Ausgabe nicht mehr ganz unnütz.
6. Nutzen für Addons
Für kleinere Addons ist OOP selten zu gebrauchen, hier gibt es
oft einen einfacheren Weg. Allerdings können größere Addons sehr
davon Profitieren Teile des Codes in Klassen zu kapseln, um so
später einfachereren Zugriff zu haben. Beispiele wären hier
UnitFrame oder ActionBar Addons. Viele neue ActionBar Addons haben
zum Beispiel eine Klasse für ihre Buttons, auch wenn die genaue
Implementieren meist anders aussieht als in diesen Beispielen.
Addons die OOP einsetzen sind unter Anderem:
- Bartender4
- Bongos3
- ag_UnitFrames
- Grid
- FuBar
- FlexBar2
Die genaue Ausführung ist oft unterschiedlich, es läuft aber immer auf das selbe hinaus.
Seiten: 1 2









