Lua-Objektorientierung

Objektorientierte Programmierung (OOP) ist eine sehr beliebte Computerprogrammierarchitektur, die Anwendungen durch Erstellen und Manipulieren von Objekten entwirft.

Die folgenden Programmiersprachen unterstützen alle objektorientierte Programmierung:

  • C++
  • Java
  • Objective-C
  • Smalltalk
  • C#
  • Ruby

Lua ist eine leichtgewichtige Skriptsprache. Obwohl sie keine eingebauten leistungsstarken objektorientierten (OO)-Funktionen wie Java oder C++ hat, ist sie sehr flexibel und kann objektorientierte Programmierung durch einige Techniken umsetzen.


Objektorientierte Merkmale

  • Kapselung: Daten und Methoden zusammenführen, Implementierungsdetails verbergen, nur notwendige Schnittstellen offenlegen und Sicherheit sowie Wartbarkeit verbessern.

  • Vererbung: Vorhandenen Code durch Ableiten neuer Klassen wiederverwenden und erweitern, wiederholtes Codieren reduzieren und Entwicklungseffizienz sowie Erweiterbarkeit verbessern.

  • Polymorphismus: Dieselbe Operation verhält sich bei verschiedenen Objekten unterschiedlich, unterstützt einheitliche Schnittstellenaufrufe und erhöht Flexibilität und Erweiterbarkeit.

  • Abstraktion: Komplexe Probleme durch Definieren von Kernklassen und Schnittstellen vereinfachen, unnötige Details verbergen, um das Komplexitätsmanagement zu erleichtern.


Objektorientierung in Lua

Wir wissen, dass Objekte aus Eigenschaften und Methoden bestehen.

Klassen in Lua können durchtable + functionsimuliert werden.

Was die Vererbung betrifft, kann sie durch Metatables simuliert werden (nicht empfohlen; für die meisten Implementierungen reicht es aus, nur die grundlegendsten Objekte zu simulieren).

In Lua ist die grundlegendste Struktur die Tabelle; wir können Tabellen verwenden, um Objekte zu erstellen.

ClassName = {}  -- 创建一个表作为类

Erstellen Sie Objekte über die new-Methode (oder andere Namen) und initialisieren Sie die Eigenschaften des Objekts.

function ClassName:new(...)
    local obj = {}  -- 创建一个新的空表作为对象
    setmetatable(obj, self)  -- 设置元表,使对象继承类的方法
    self.__index = self  -- 设置索引元方法
    -- 初始化对象的属性
    obj:init(...)  -- 可选:调用初始化函数
    return obj
end

Eine Tabelle ist der grundlegendste zusammengesetzte Datentyp in Lua und kann verwendet werden, um Objekteigenschaften darzustellen.

Die Funktion in Lua kann verwendet werden, um Methoden darzustellen:

function ClassName:sayHello()
    print("Hello, my name is " .. self.name)
end

Verwenden Sie die new-Methode, um Objekte zu erstellen, und rufen Sie die Methoden der Klasse über das Objekt auf.

local obj = ClassName:new("Alice")  -- 创建对象
obj:sayHello()  -- 调用对象的方法

In Lua kann eine Tabelle als Variante eines Objekts betrachtet werden. Wie Objekte haben Tabellen einen Zustand (Membervariablen) und können unabhängige Entitäten darstellen.

Tabellen haben nicht nur Datenelemente, sondern können auch Memberfunktionen enthalten, die Objektmethoden ähneln:

Beispiel

-- Definieren der Klasse Person
Person = {name = "", age = 0}

-- Konstruktor von Person
function Person:new(name, age)
    local obj = {}  -- Erstellen einer neuen Tabelle als Objekt
    setmetatable(obj, self)  -- Setze die Metatabelle, um es zu einer Instanz von Person zu machen
    self.__index = self  -- Setze die Index-Metamethode so, dass sie auf Person verweist
    obj.name = name
    obj.age = age
    return obj
end

-- Füge eine Methode hinzu: persönliche Informationen ausgeben
function Person:introduce()
    print("Mein Name ist " .. self.name .. " und ich bin " .. self.age .. " Jahre alt.")
end

Code-Erklärung:

  • Personist eine Tabelle mit zwei Eigenschaften: name und age; diese beiden Eigenschaften sind die Standardeigenschaften der Klasse.
  • Person:new(name, age)ist ein Konstruktor, der verwendet wird, um neue Person-Objekte zu erstellen.
  • local obj = {}Erstelle eine neue Tabelle als das Objekt; setmetatable(obj, self) setzt die Metatabelle, sodass die Tabelle zu einer Instanz der Klasse Person wird.
  • self.__index = selfSetze die Index-Metamethode, sodass obj auf die Eigenschaften und Methoden der Klasse Person zugreifen kann.
  • introduceist eine Methode der Klasse Person, die den Namen und das Alter des Person-Objekts ausgibt.

Methoden aufrufen:

-- 创建一个 Person 对象
local person1 = Person:new("Alice", 30)

-- 调用对象的方法
person1:introduce()  -- 输出 "My name is Alice and I am 30 years old."

Ein einfaches Beispiel

Die folgende einfache Klasse enthält drei Eigenschaften: area, length und breadth. Die Methode printArea wird verwendet, um das Berechnungsergebnis auszugeben:

Beispiel

-- Definiere die Klasse Rectangle
Rectangle = {area = 0, length = 0, breadth = 0}

-- Konstruktor zum Erstellen von Rectangle-Objekten
function Rectangle:new(o, length, breadth)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle, damit sie die Methoden von Rectangle erbt
  self.__index = self  -- Stelle sicher, dass Methoden und Eigenschaften beim Zugriff gefunden werden
  o.length = length or 0  -- length festlegen, Standardwert 0
  o.breadth = breadth or 0  -- Setze breadth, Standardwert 0
  o.area = o.length * o.breadth  -- Berechne die Fläche
  return o
end

-- Gib die Fläche des Rechtecks aus
function Rectangle:printArea()
  print("Die Fläche des Rechtecks ist ", self.area)
end

Objekte erstellen

Das Erstellen eines Objekts ist der Prozess der Speicherzuweisung für eine Instanz einer Klasse; jede Klasse hat ihren eigenen Speicher und teilt öffentliche Daten:

r = Rectangle:new(nil,10,20)

Zugriff auf Eigenschaften

Wir können den Punkt.verwenden, um auf die Eigenschaften der Klasse zuzugreifen:

print(r.length)

Zugriff auf Memberfunktionen

Wir können den Doppelpunkt:verwenden, um auf die Memberfunktionen der Klasse zuzugreifen:

r:printArea()

Speicher wird zugewiesen, wenn Objekte initialisiert werden.

Vollständiges Beispiel

Im Folgenden demonstrieren wir ein vollständiges Beispiel für objektorientierte Programmierung in Lua:

Beispiel

-- Definiere die Klasse Rectangle
Rectangle = {area = 0, length = 0, breadth = 0}

-- Konstruktor zum Erstellen von Rectangle-Objekten
function Rectangle:new(o, length, breadth)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle, damit sie die Methoden von Rectangle erbt
  self.__index = self  -- Stelle sicher, dass Methoden und Eigenschaften beim Zugriff gefunden werden
  o.length = length or 0  -- length festlegen, Standardwert 0
  o.breadth = breadth or 0  -- Setze breadth, Standardwert 0
  o.area = o.length * o.breadth  -- Berechne die Fläche
  return o
end

-- Gib die Fläche des Rechtecks aus
function Rectangle:printArea()
  print("Die Fläche des Rechtecks ist ", self.area)
end

-- Führe das Beispiel aus:
local rect1 = Rectangle:new(nil, 5, 10)  -- Erstelle ein Rechteck mit Länge 5 und Breite 10
rect1:printArea()  -- Ausgabe "Die Fläche des Rechtecks beträgt 50"

local rect2 = Rectangle:new(nil, 7, 3)  -- Erstelle ein Rechteck mit Länge 7 und Breite 3
rect2:printArea()  -- Ausgabe "Die Fläche des Rechtecks beträgt 21"

Das Ausführen des obigen Programms erzeugt die folgende Ausgabe:

矩形面积为      50
矩形面积为      21

Lua-Vererbung

Vererbung bedeutet, dass ein Objekt direkt die Eigenschaften und Methoden eines anderen Objekts verwendet und dazu verwendet werden kann, die Eigenschaften und Methoden einer Basisklasse zu erweitern.

Vererbung in Lua wird implementiert, indem die Metatabelle der Unterklasse festgelegt wird.

Wir können eine neue Tabelle erstellen und ihre Metatabelle auf die Elternklasse setzen.

Im folgenden Beispiel erbt die Square-Klasse die Eigenschaften und Methoden der Rectangle-Klasse und nimmt auf dieser Basis Änderungen vor.

-- Definiere die Klasse Rectangle
Rectangle = {area = 0, length = 0, breadth = 0}

-- Konstruktor zum Erstellen von Rectangle-Objekten
function Rectangle:new(o, length, breadth)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle, damit sie die Methoden von Rectangle erbt
  self.__index = self  -- Stelle sicher, dass Methoden und Eigenschaften beim Zugriff gefunden werden
  o.length = length or 0  -- length festlegen, Standardwert 0
  o.breadth = breadth or 0  -- Setze breadth, Standardwert 0
  o.area = o.length * o.breadth  -- Berechne die Fläche
  return o
end

-- Gib die Fläche des Rechtecks aus
function Rectangle:printArea()
  print("Die Fläche des Rechtecks ist ", self.area)
end

-- Definiere die Square-Klasse, die von der Rectangle-Klasse erbt
Square = Rectangle:new()  -- Square erbt von der Rectangle-Klasse

-- Konstruktor überschreiben (alle Seiten eines Quadrats sind gleich)
function Square:new(o, side)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle, damit sie die Methoden von Rectangle erbt
  self.__index = self  -- Stelle sicher, dass Methoden und Eigenschaften beim Zugriff gefunden werden
  o.length = side or 0  -- Seitenlänge festlegen
  o.breadth = side or 0  -- Die Breite und Länge des Quadrats sind gleich
  o.area = o.length * o.breadth  -- Berechne die Fläche
  return o
end

-- Führe das Beispiel aus:
local rect = Rectangle:new(nil, 5, 10)  -- Erstelle ein Rechteck mit Länge 5 und Breite 10
rect:printArea()  -- Ausgabe "Die Fläche des Rechtecks ist 50"

local square = Square:new(nil, 4)  -- Erstelle ein Quadrat mit Seitenlänge 4
square:printArea()  -- Ausgabe "Die Fläche des Rechtecks ist 16"

RectangleClass: ist immer noch die grundlegende Rectangle-Klasse, dielength、breadthundareaEigenschaften sowie Methoden zum Berechnen und Ausgeben der Fläche.

SquareDie Klasse erbt vonRectangle:SquareDie Klasse verwendetRectangle:new()um zu erbenRectangleMethoden und Eigenschaften der Klasse. Da die Länge und Breite eines Quadrats gleich sind, haben wirSquare:newden Konstruktor in der Methode überschrieben und dabeilengthundbreadthauf denselben Wert gesetzt (d. h.side)。

Überschreiben des Konstruktors:Square:new(o, side)Wenn die Methode ein Square-Objekt erstellt, verwendet sie die übergebene Seitenlängesidezum Initialisierenlengthundbreadthder Eigenschaften und berechnet die Fläche.

Ausführungsergebnis:

矩形面积为  50
矩形面积为  16

Funktionsüberschreibung

In Lua bezeichnet das Überschreiben von Funktionen (auch Methodenüberschreibung genannt) die Neudefinition oder Ersetzung einer vorhandenen Methode der Elternklasse durch die Unterklasse während der Vererbung.

Eine Unterklasse kann das Verhalten von Methoden der Elternklasse nach Bedarf ändern oder erweitern.

Im obigen Beispiel überschreibt die Klasse Square den Konstruktor der Klasse Rectangle und ändert dadurch die Art und Weise, wie Objekte initialisiert werden, insbesondere indem Länge und Breite des Rechtecks auf denselben Wert gesetzt werden, weil das charakteristische Merkmal eines Quadrats gleiche Seitenlängen sind.

Als Nächstes verwenden wir eine Animal-Klasse und eine Dog-Klasse, die von ihr erbt, um zu demonstrieren, wie Methoden überschrieben werden.

-- Definiere die Animal-Klasse
Animal = {name = "Unbekannt"}

-- Konstruktor der Animal-Klasse
function Animal:new(o, name)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle, damit sie die Methoden von Animal erbt
  self.__index = self  -- Erlaube dem Objekt, auf die Methoden von Animal zuzugreifen
  o.name = name or "Unbekannt"  -- Setze den Namen, Standard ist "Unbekannt"
  return o
end

-- Methode der Animal-Klasse: Geräusch machen
function Animal:speak()
  print(self.name .. " macht ein Geräusch.")
end


-- Definiere die Dog-Klasse, die von Animal erbt
Dog = Animal:new()  -- Dog erbt von der Animal-Klasse

-- Überschreibe den Konstruktor der Dog-Klasse
function Dog:new(o, name, breed)
  o = o or {}  -- Wenn kein Objekt übergeben wird, erstelle eine neue leere Tabelle
  setmetatable(o, self)  -- Setze die Metatabelle so, dass sie Methoden von Dog und Animal erbt
  self.__index = self  -- Erlaube dem Objekt, auf die Methoden von Dog zuzugreifen
  o.name = name or "Unbekannt"
  o.breed = breed or "Unbekannt"
  return o
end

-- Überschreibe die sound-Methode der Dog-Klasse (überschreibt die speak-Methode von Animal)
function Dog:speak()
  print(self.name .. " bellt.")
end


-- Erstelle ein Animal-Objekt
local animal = Animal:new(nil, "Generisches Tier")
animal:speak()  -- Ausgabe "Generisches Tier gibt ein Geräusch von sich."

-- Erstelle ein Dog-Objekt
local dog = Dog:new(nil, "Buddy", "Golden Retriever")
dog:speak()  -- Ausgabe "Buddy barks."
  • AnimalKlasse: definiert eine BasisklasseAnimal, die hatnameEigenschaften undspeakMethoden.speakDie Methode ist eine Standardimplementierung, die "some animal makes a sound." ausgibt.

  • DogKlassenvererbungAnimal:DogDie Klasse erbt vonAnimal, und durch dieDog:new()Methode erstellt ihre eigenen Instanzen.

  • ÜberschreibenspeakMethode: in derDogKlasse, überschrieb diespeakMethode, die ihr Verhalten von der "make sound"-Methode der Elternklasse zu "dog barking" ändert. Dies ist eine Ausprägung von Methodenüberschreibung. Die Unterklasse (Dog) hat die Methode der Elternklasse (Animal) geändert.

Run result:

Generic Animal makes a sound.
Buddy barks.

Polymorphismus

Lua-Polymorphismus wird durch Metatabellen und Methodenüberschreibung implementiert. Wenn Objekte unterschiedlicher Typen dieselbe Methode aufrufen, führt Lua unterschiedliche Methoden basierend auf dem tatsächlichen Typ des Objekts aus.

Beispiel

-- Definiere eine "Klasse" (eigentlich eine Tabelle)
Person = {}

-- Füge der "Klasse" einen Konstruktor hinzu
function Person:new(name, age)
    local obj = {}  -- Erstellen einer neuen Tabelle als Objekt
    setmetatable(obj, self)  -- Setze die Metatabelle, um anzuzeigen, dass es sich um eine Instanz der Person-Klasse handelt
    self.__index = self  -- Setze die Index-Metamethode so, dass sie auf Person verweist
    obj.name = name
    obj.age = age
    return obj
end

-- Methoden hinzufügen
function Person:greet()
    print("Hallo, mein Name ist " .. self.name)
end

-- Definiere eine Unterklasse Student, die von Person erbt
Student = Person:new()

-- Die Unterklasse überschreibt die Methode der Elternklasse
function Student:greet()
    print("Hallo, ich bin Student und mein Name ist " .. self.name)
end

local person2 = Person:new("Charlie", 25)
local student2 = Student:new("David", 18)

-- Polymorphismus: Objekte unterschiedlicher Typen rufen dieselbe Methode auf
person2:greet()  -- Ausgabe "Hallo, mein Name ist Charlie"
student2:greet()  -- Ausgabe "Hallo, ich bin Student und mein Name ist David"

Obwohl person2 und student2 dieselbe greet-Methode aufrufen, ruft Lua für jedes die passende Version auf, weil ihre Typen unterschiedlich sind.

Run result:

Hello, my name is Charlie
Hi, I'm a student and my name is David

Weitere objektorientierte Konzepte

Kapselung

Kapselung wird üblicherweise implementiert, indem Daten und Methoden in einer Tabelle gekapselt werden. Wir können Kapselung simulieren, indem wir den Zugriff auf die Tabelle steuern, zum Beispiel mit Metamethoden, um externen Zugriff einzuschränken.

Beispiel

-- Definiere eine "Klasse" (eigentlich eine Tabelle)
Person = {}

-- Kapselung hinzufügen: Eigenschaften verbergen
function Person:new(name, age)
    local obj = {}
    setmetatable(obj, self)
    self.__index = self
    obj.name = name
    obj.age = age
    return obj
end

function Person:setName(name)
    self.name = name  -- Methode zum Ändern des Namens bereitstellen
end

function Person:getName()
    return self.name  -- Methode zum Abrufen des Namens bereitstellen
end

Auf diese Weise können wir den Zugriff auf Eigenschaften steuern und Kapselung simulieren.

Abstraktion

Abstraktion bedeutet, komplexe Dinge zu vereinfachen, indem unnötige Details verborgen werden. Obwohl Lua selbst das Konzept von Klassen nicht hat, können wir Abstraktion durch Kapselung erreichen.

Beispiel

-- Nur die Schnittstelle offenlegen, nicht die Implementierungsdetails
function Person:showInfo()
    print("Name: " .. self.name)
    print("Age: " .. self.age)
end
Weitere Erweiterungen