[Notas de estudio de Lua] Lua Advanced - Tabla (4) Herencia, encapsulación y polimorfismo

Insertar descripción de la imagen aquí

Directorio de artículos


encapsulación

// 定义基类
Object = {
    
    }

//由于表的特性,该句就相当于定义基类变量
Object.id =1

//该句相当于定义方法,Object可以视为定义的对象,Test可以视为方法名
//我们知道Object是一个表,但是抽象地看,请把Object看着面向对象中的 “对象”
function Object:Test()
    print(self.id)
end
// 以上语句等同于:
// public class Object{
    
    
	int id=1;
	void Test(Object obj)
		print(obj.id);
}


//定义一个new方法,用于创建这个基类的对象
function Object:new()
	//定义空表obj,用面向对象比喻相当于new了一个空对象
    local obj = {
    
    }
    //绑定元表,将元表看作一个基类
    self.__index = self
    setmetatable(obj, self)
    //返回空对象
    return obj
end

local Car = Object:new()  //实际是将new出的空对象return给外部定义的Car
// 以上语句等同于:
// Object Car = new Object();

// 由于Car实际上是空的table,所以访问Car其实是通过__index访问基类中的索引
// 相当于虽然没有定义Car内的变量,但初始化时继承基类的值作为了初始值
print(Car.id) --1,来自元表

// 同样的,Car实际使用了__index基类提供的方法
// 但是由于入参是self,此处就是Car,print(Car.id),最终还是访问了基类__index找到的Object.id
Car:Test() --1,来自元表

// 定义Car中的变量
Car.id = 2
// 现在Car表中有了索引id,那么就能找到这个索引,所以输出为2
Car:Test() --2,来自子表

Ahora podemos crear un nuevo objeto correspondiente a la clase base como si estuviera orientado a objetos. Pero lo nuevo aquí no es completamente similar al nuevo orientado a objetos, por ejemplo, podemos hacer esto:

Car.name = "a"
print(Car.name)
输出:
a

Cuando encapsulamos la clase Objeto, no hay ningún índice de nombre. En Lua, creamos un nuevo objeto y agregamos algunas variables y métodos nuevos. Estas características obviamente solo están disponibles para las subclases que heredan la clase principal. No es malo, pero si queremos una encapsulación completa podemos imponer algunas restricciones:

//定义一个垃圾列表,将添加到子类的垃圾都丢进去
garbage={
    
    }

//定义一个new方法,用于创建这个基类的对象
function Object:new()
	//定义空表obj,用面向对象比喻相当于new了一个空对象
    local obj = {
    
    }
    // 禁止子类的添加
    self.__newindex = garbage
    //绑定元表,将元表看作一个基类
    self.__index = self
    setmetatable(obj, self)
    //返回空对象
    return obj
end
local Car = Object:new()
Car.name = "a"
print(Car.name)

输出:
nil

De hecho, ahora hemos implementado la encapsulación, que no solo puede acceder a los métodos y variables de la clase base, sino que también puede evitar otras cosas recién agregadas, pero la basura debe limpiarse a tiempo, lo que explicaremos más adelante en la recolección de basura.


heredar

La herencia es una característica importante de la orientación a objetos. Un nuevo objeto por sí solo no puede satisfacer todas las necesidades. Si queremos anular algunos métodos de la clase principal en lugar de usarlos directamente, necesitamos herencia.

Observando el Object:new()código anterior, de hecho, si queremos usarlo para herencia, solo necesitamos cambiarlo.

Object = {
    
    }
Object.id = 1;
function Object:Test()
    print(self.id)
end

//换种方式,如果我们不return的话,想要返回这个值,可以直接把它丢进全局表中
function Object:subClass(className)
    _G[className] = {
    
    }
    self.__index = self
    setmetatable(_G[className], self)
end
Object:subClass("Cat")
print(Cat.id)
输出:
1

La herencia es un poco más simple que la encapsulación, de hecho, es exactamente igual a la encapsulación que definimos primero, pero se implementa de una manera diferente.

// new一个Cat类的对象
local WhiteCat = Cat:new()
print(WhiteCat.id) -- 1
function Object:Test()
    print("我是基类")
end

function Object:new()
    local obj = {
    
    }
    self.__newindex = garbage
    self.__index = self
    setmetatable(obj, self)
    return obj
end

function Object:subClass(className)
    _G[className] = {
    
    }
    self.__index = self
    setmetatable(_G[className], self)
end

//Cat继承基类
Object:subClass("Cat")
//new一个Cat类的对象WhiteCat
local WhiteCat = Cat:new()
WhiteCat:Test()  -- 我是基类

// 重写Test方法(其实只是新写了一个放在Cat表里被调用,更像重载?)
function Cat:Test()
    print("我是猫类")
end
WhiteCat:Test()  --我是猫类

//想要重写Cat的Test方法?不好意思我已经用__newindex封装好了
//白猫是个对象,而不是Cat这个类,它不应该重写方法
//下面重写的方法会被丢到garbage里
function WhiteCat:Test()
    print("我是白猫")
end
WhiteCat:Test() --我是猫类
garbage:Test() --我是白猫

Si no lo comprende, se recomienda volver a aprender Tabla, metatabla y orientada a objetos.


Polimorfismo

El polimorfismo significa que para el mismo método de una clase principal, las subclases pueden ejecutar una lógica diferente. ¿Qué podemos hacer para lograr el polimorfismo?

  1. Métodos de anulación y sobrecarga
  2. implementar interfaz
  3. Implementar clases abstractas y métodos abstractos.

Si se reescribe debería ser así:

function Object:Test()
    print("我是基类")
end
Object:subClass("Cat")
Object:subClass("Dog")
function Cat:Test()
    print("我是猫类")
end
function Dog:Test()
    print("我是狗?")
end

Aunque se puede lograr la reescritura, el problema es que la reescritura después de heredar la clase principal no puede conservar el método con el mismo nombre de la clase principal, entonces, ¿qué debo hacer si quiero acceder al método de la clase principal?

No olvide que nuestra clase es en realidad una tabla. ¿No puedo simplemente almacenar la clase principal directamente en ella y luego acceder a ella cuando quiera usarla? De todos modos, no existen restricciones de sintaxis orientada a objetos.

function Object:subClass(className)
    _G[className] = {
    
    }
    local obj = _G[className]
    self.__index = self
    // 直接把父类表存进子类的base
    obj.base = self
    setmetatable(obj , self)
end

function Dog:Test()
    print("我是狗?")
end
Dog:Test()
Dog.base:Test()

输出:
我是狗?
我是基类

function Dog:Test()
	// 如果想在继承了父类的方法的基础之上重写
    self.base:Test()
    print("我是狗?")
end

Dog:Test() --我是基类 我是狗?

Tenga en cuenta que si usamos el método de la clase principal directamente, debemos evitar que diferentes clases compartan variables globales al llamar a la clase principal:

Object = {
    
    }
Object.id = 1;
function Object:Test()
    self.id = self.id + 1
    print(self.id)
end

Object:subClass("Cat")
function Cat:Test()
	self.base:Test()
    print("我是猫类")
end
Object:subClass("Dog")
function Dog:Test()
    self.base:Test()
    print("我是狗?")
end

输出:
2
我是猫类
3
我是狗?

La razón también es muy simple: tablela clase principal se almacena en la memoria table. Si llamamos directamente al método de la clase principal Test, self.idcada llamada aumentará en uno. La clase principal en los dos tabletiene la misma dirección, y esto es lo que se Object:Test()pasa cada vez en este método , es decir, la clase principal en sí, por lo que lo que se agrega está en la clase principal . Como variable global, es naturalmente constante Aumentado.selfxxx.basetableself.idid

Entonces, si queremos que la subclase pueda Object:Test()reescribir basándose en la herencia del método de la clase principal, y también queremos self.idcambiar selfla tabla de la subclase donde usamos el método, entonces debemos escribirlo así:

Object = {
    
    }
Object.id = 1;
function Object:Test()
    self.id = self.id + 1
    print(self.id)
end

Object:subClass("Cat")
function Cat:Test()
	// 手动地传入参数,因为冒号传入给self的是base
	// 因此需要手动地改变传入的参数的值
	self.base.Test(self)
    print("我是猫类")
end
Cat:Test()

输出:
2
我是猫类

La sobrecarga debería ser el método polimórfico más simple: solo necesita cambiar el número de parámetros de entrada de la función.

En cuanto a las interfaces y clases abstractas, las funciones propias de Lua se pueden reescribir y la abstracción sigue siendo muy fuerte. En cuanto a la interfaz, deberíamos poder acceder a otra estructura de tabla para implementarla, por ejemplo, self.base debería considerarse como una interfaz, por supuesto, estos son solo mis pensamientos y aún no lo he aprendido.

Supongo que te gusta

Origin blog.csdn.net/milu_ELK/article/details/131978479
Recomendado
Clasificación