Lua orientado a objetos
La programación orientada a objetos (Object Oriented Programming, OOP) es una arquitectura de programación de computadoras muy popular que diseña aplicaciones mediante la creación y manipulación de objetos.
Los siguientes lenguajes de programación admiten la programación orientada a objetos:
- C++
- Java
- Objective-C
- Smalltalk
- C#
- Ruby
Lua es un lenguaje de script liviano. Aunque no tiene características de orientación a objetos (OO) integradas tan potentes como Java o C++, es muy flexible y puede implementar la programación orientada a objetos mediante algunos trucos.
Características de la orientación a objetos
Encapsulamiento: agrupa datos y métodos, oculta los detalles de implementación, expone solo las interfaces necesarias y mejora la seguridad y la mantenibilidad.
Herencia: reutiliza y extiende el código existente mediante la derivación de nuevas clases, reduce la codificación repetida y mejora la eficiencia del desarrollo y la escalabilidad.
Polimorfismo: la misma operación se comporta de manera diferente en distintos objetos, admite llamadas a través de una interfaz unificada y aumenta la flexibilidad y la extensibilidad.
Abstracción: simplifica problemas complejos, define clases e interfaces centrales, oculta detalles innecesarios y facilita la gestión de la complejidad.
Orientación a objetos en Lua
Sabemos que los objetos están compuestos por propiedades y métodos.
Las clases en Lua se puedentable + functionsimular.
En cuanto a la herencia, se puede simular mediante metetable (no se recomienda su uso, es suficiente para simular solo los objetos más básicos en la mayoría de las implementaciones).
En Lua, la estructura más básica es table; podemos usar tablas (table) para crear objetos.
ClassName = {} -- 创建一个表作为类
Cree objetos mediante el método new (u otro nombre) e inicialice las propiedades del objeto.
function ClassName:new(...)
local obj = {} -- 创建一个新的空表作为对象
setmetatable(obj, self) -- 设置元表,使对象继承类的方法
self.__index = self -- 设置索引元方法
-- 初始化对象的属性
obj:init(...) -- 可选:调用初始化函数
return obj
end
La tabla (table) es el tipo de datos compuesto más básico en Lua y se puede usar para representar las propiedades de un objeto.
La función function en Lua se puede usar para representar métodos:
function ClassName:sayHello()
print("Hello, my name is " .. self.name)
end
Use el método new para crear objetos y llame a los métodos de la clase a través del objeto.
local obj = ClassName:new("Alice") -- 创建对象
obj:sayHello() -- 调用对象的方法
En Lua, la tabla (table) puede considerarse una variante de objeto. Al igual que los objetos, las tablas tienen estado (variables miembro) y pueden representar entidades independientes.
Las tablas no solo tienen miembros de datos, sino que también pueden contener funciones miembro similares a los métodos de objetos:
Ejemplo
Person = {name = "", age = 0}
-- Constructor de Person
function Person:new(name, age)
local obj = {} -- Crear una nueva tabla como objeto
setmetatable(obj, self) -- Establecer la metatabla para que sea una instancia de Person
self.__index = self -- Establecer el metamétodo de indexación para que apunte a Person
obj.name = name
obj.age = age
return obj
end
-- Agregar método: imprimir información personal
function Person:introduce()
print("My name is " .. self.name .. " and I am " .. self.age .. " years old.")
end
Explicación del código:
- Persones una tabla que tiene dos propiedades: name y age, estas dos propiedades son las propiedades predeterminadas de la clase.
- Person:new(name, age)es un constructor que se utiliza para crear nuevos objetos Person.
- local obj = {}crea una nueva tabla como objeto; setmetatable(obj, self) establece la metatabla para que la tabla se convierta en una instancia de la clase Person.
- self.__index = selfestablece el metamétodo de indexación para que obj pueda acceder a las propiedades y métodos de la clase Person.
- introducees un método de la clase Person que imprime el nombre y la edad del objeto Person.
Llamar a los métodos:
-- 创建一个 Person 对象
local person1 = Person:new("Alice", 30)
-- 调用对象的方法
person1:introduce() -- 输出 "My name is Alice and I am 30 years old."
Un ejemplo simple
La siguiente clase simple contiene tres propiedades: area, length y breadth; el método printArea se utiliza para imprimir el resultado del cálculo:
Ejemplo
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor para crear objetos Rectangle
function Rectangle:new(o, length, breadth)
o = o or {} -- Si no se pasa un objeto, crear una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Rectangle
self.__index = self -- Asegurar que los métodos y propiedades se encuentren al acceder
o.length = length or 0 -- Establecer la longitud, por defecto 0
o.breadth = breadth or 0 -- Establecer el ancho, por defecto 0
o.area = o.length * o.breadth -- Calcular el área
return o
end
-- Imprimir el área del rectángulo
function Rectangle:printArea()
print("El área del rectángulo es ", self.area)
end
Crear objetos
Crear objetos es el proceso de asignar memoria para las instancias de una clase. Cada clase tiene su propia memoria y comparte datos comunes.
r = Rectangle:new(nil,10,20)
Acceder a las propiedades
Podemos usar el punto.para acceder a las propiedades de la clase:
print(r.length)
Acceder a las funciones miembro
Podemos usar los dos puntos:para acceder a las funciones miembro de la clase:
r:printArea()
La memoria se asigna cuando se inicializa el objeto.
Ejemplo completo
A continuación demostramos un ejemplo completo de orientación a objetos en Lua:
Ejemplo
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor para crear objetos Rectangle
function Rectangle:new(o, length, breadth)
o = o or {} -- Si no se pasa un objeto, crear una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Rectangle
self.__index = self -- Asegurar que los métodos y propiedades se encuentren al acceder
o.length = length or 0 -- Establecer la longitud, por defecto 0
o.breadth = breadth or 0 -- Establecer el ancho, por defecto 0
o.area = o.length * o.breadth -- Calcular el área
return o
end
-- Imprimir el área del rectángulo
function Rectangle:printArea()
print("El área del rectángulo es ", self.area)
end
-- Ejecutar el ejemplo:
local rect1 = Rectangle:new(nil, 5, 10) -- Crear un rectángulo de largo 5 y ancho 10
rect1:printArea() -- Salida "El área del rectángulo es 50"
local rect2 = Rectangle:new(nil, 7, 3) -- Crear un rectángulo de largo 7 y ancho 3
rect2:printArea() -- Salida "El área del rectángulo es 21"
Al ejecutar el programa anterior, el resultado es:
矩形面积为 50 矩形面积为 21
Herencia en Lua
La herencia se refiere a que un objeto utiliza directamente las propiedades y métodos de otro objeto, y se puede usar para extender las propiedades y métodos de la clase base.
La herencia en Lua se implementa estableciendo la metatabla de la subclase.
Podemos crear una nueva tabla y establecer su metatabla como la clase padre.
En el siguiente ejemplo, la clase Square heredará las propiedades y métodos de la clase Rectangle y hará modificaciones sobre esta base.
Rectangle = {area = 0, length = 0, breadth = 0}
-- Constructor para crear objetos Rectangle
function Rectangle:new(o, length, breadth)
o = o or {} -- Si no se pasa un objeto, crear una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Rectangle
self.__index = self -- Asegurar que los métodos y propiedades se encuentren al acceder
o.length = length or 0 -- Establecer la longitud, por defecto 0
o.breadth = breadth or 0 -- Establecer el ancho, por defecto 0
o.area = o.length * o.breadth -- Calcular el área
return o
end
-- Imprimir el área del rectángulo
function Rectangle:printArea()
print("El área del rectángulo es ", self.area)
end
-- Definir la clase Square, hereda de la clase Rectangle
Square = Rectangle:new() -- Square hereda la clase Rectangle
-- Sobrescribir el constructor (los lados del cuadrado son iguales)
function Square:new(o, side)
o = o or {} -- Si no se pasa un objeto, crear una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Rectangle
self.__index = self -- Asegurar que los métodos y propiedades se encuentren al acceder
o.length = side or 0 -- Establecer la longitud del lado
o.breadth = side or 0 -- El ancho y el largo del cuadrado son iguales
o.area = o.length * o.breadth -- Calcular el área
return o
end
-- Ejecutar el ejemplo:
local rect = Rectangle:new(nil, 5, 10) -- Crear un rectángulo de largo 5 y ancho 10
rect:printArea() -- Salida "El área del rectángulo es 50"
local square = Square:new(nil, 4) -- Crear un cuadrado con lado de longitud 4
square:printArea() -- Salida "El área del rectángulo es 16"
RectangleClase: sigue siendo la clase básica del rectángulo, tienelength、breadthyareapropiedades, así como métodos para calcular e imprimir el área.
SquareLa clase hereda deRectangle:Squarela clase a través deRectangle:new()para heredarRectanglelos métodos y propiedades de la clase. Dado que el largo y el ancho del cuadrado son iguales, enSquare:newsobrescribimos el constructor en el método, y establecemoslengthybreadthal mismo valor (es decir,side)。
Sobrescribir el constructor:Square:new(o, side)Cuando el método crea un objeto cuadrado, usa el lado pasado como parámetrosidepara inicializarlengthybreadthlas propiedades, y calcula el área.
Resultado de la ejecución:
矩形面积为 50 矩形面积为 16
Sobrescritura de funciones
En Lua, la sobrescritura de funciones (también llamada sobrescritura de métodos) se refiere a la redefinición o reemplazo, durante la herencia, de métodos ya existentes de la clase padre por parte de la subclase.
La subclase puede modificar o extender el comportamiento de los métodos de la clase padre según sea necesario.
En el ejemplo anterior, la clase Square sobrescribe el constructor de la clase Rectangle, cambiando así la forma de inicialización del objeto, en particular estableciendo length y breadth del rectángulo al mismo valor, ya que la característica del cuadrado es que sus lados son iguales.
A continuación, mostramos cómo sobrescribir métodos mediante una clase Animal y una clase Dog que hereda de ella.
Animal = {name = "Unknown"}
-- Constructor de la clase Animal
function Animal:new(o, name)
o = o or {} -- Si no se pasa un objeto, se crea una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Animal
self.__index = self -- Permitir que el objeto acceda a los métodos de Animal
o.name = name or "Unknown" -- Establecer el nombre, por defecto "Unknown"
return o
end
-- Método de la clase Animal: sonido
function Animal:speak()
print(self.name .. " makes a sound.")
end
-- Definir la clase perro (Dog), heredada de Animal
Dog = Animal:new() -- Dog hereda de la clase Animal
-- Sobrescribir el constructor de la clase Dog
function Dog:new(o, name, breed)
o = o or {} -- Si no se pasa un objeto, se crea una nueva tabla vacía
setmetatable(o, self) -- Establecer la metatabla para heredar los métodos de Dog y Animal
self.__index = self -- Permitir que el objeto acceda a los métodos de Dog
o.name = name or "Unknown"
o.breed = breed or "Unknown"
return o
end
-- Sobrescribir el método de sonido de la clase Dog (sobrescribir el método speak de Animal)
function Dog:speak()
print(self.name .. " barks.")
end
-- Crear un objeto Animal
local animal = Animal:new(nil, "Generic Animal")
animal:speak() -- Salida "Generic Animal makes a sound."
-- Crear un objeto Dog
local dog = Dog:new(nil, "Buddy", "Golden Retriever")
dog:speak() -- Salida "Buddy barks."
AnimalClase: se define una clase baseAnimal, que tienenamepropiedades yspeakmétodos.speakEl método es una implementación predeterminada que muestra "un animal hace un sonido".DogLa clase heredaAnimal:DogLa clase hereda deAnimal, y medianteDog:new()el método crea su propia instancia.Sobrescribir el
speakmétodo: en laDogclase, se sobrescribe elspeakmétodo, cambiando su comportamiento de "hacer sonidos" de la clase padre a "ladrido de perro". Esto es una manifestación de la sobrescritura de métodos: la subclase (Dog) cambia el comportamiento del método de la clase padre (Animal).
Resultado de la ejecución:
Generic Animal makes a sound. Buddy barks.
Polimorfismo
El polimorfismo de Lua se implementa mediante metatablas y sobrescritura de métodos. Cuando objetos de diferentes tipos llaman al mismo método, Lua ejecuta diferentes métodos según el tipo real del objeto.
Ejemplo
Person = {}
-- Agregar un constructor a la "clase"
function Person:new(name, age)
local obj = {} -- Crear una nueva tabla como objeto
setmetatable(obj, self) -- Establecer la metatabla para indicar que es una instancia de la clase Person
self.__index = self -- Establecer el metamétodo de índice, apuntando a Person
obj.name = name
obj.age = age
return obj
end
-- Agregar método
function Person:greet()
print("Hello, my name is " .. self.name)
end
-- Definir una subclase Student que hereda de Person
Student = Person:new()
-- La subclase sobrescribe el método de la clase padre
function Student:greet()
print("Hi, I'm a student and my name is " .. self.name)
end
local person2 = Person:new("Charlie", 25)
local student2 = Student:new("David", 18)
-- Polimorfismo: objetos de diferentes tipos llaman al mismo método
person2:greet() -- Salida "Hello, my name is Charlie"
student2:greet() -- Salida "Hi, I'm a student and my name is David"
Aunque person2 y student2 llaman al mismo método greet, como sus tipos son diferentes, Lua llama a la versión adecuada para cada uno.
Resultado de la ejecución:
Hello, my name is Charlie Hi, I'm a student and my name is David
Otros conceptos de la orientación a objetos
Encapsulamiento
El encapsulamiento generalmente se implementa encapsulando datos y métodos en una tabla. Podemos simular el encapsulamiento controlando los permisos de acceso de la tabla, por ejemplo, usando metamétodos para restringir el acceso externo.
Ejemplo
Person = {}
-- Agregar encapsulamiento: ocultar propiedades
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 -- Proporcionar un método para modificar name
end
function Person:getName()
return self.name -- Proporcionar un método para obtener name
end
De esta manera, podemos controlar el acceso a las propiedades y simular el encapsulamiento.
Abstracción
La abstracción se refiere a simplificar cosas complejas y ocultar detalles innecesarios. Aunque Lua en sí no tiene el concepto de clases, podemos lograr la abstracción mediante el encapsulamiento.
Ejemplo
function Person:showInfo()
print("Name: " .. self.name)
print("Age: " .. self.age)
end