[Lua Study Notes] Lua Advanced - Table (4) Inheritance, Encapsulation, Polymorphism

Insert image description here

Article directory


encapsulation

// 定义基类
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,来自子表

Now we can create a new object corresponding to the base class just like object-oriented. But the new here is not completely similar to the object-oriented new. For example, we can do this:

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

When we encapsulate the Object class, there is no index of name at all. In Lua, we create a new object and add some new variables and methods. These features are obviously only available to subclasses that inherit the parent class. It’s not a bad thing, but if we want full encapsulation we can impose some restrictions:

//定义一个垃圾列表,将添加到子类的垃圾都丢进去
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

Now we have indeed implemented encapsulation, which can not only access the methods and variables of the base class, but also prevent other newly added things, but the garbage must be cleaned up in time, which we will explain in the garbage collection later.


inherit

Inheritance is an important feature of object-oriented. A new object alone cannot meet all needs. If we want to override some methods of the parent class instead of using them directly, we need inheritance.

Observing the above Object:new()code, in fact, if we want to use it for inheritance, we only need to change it.

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

Inheritance is a little simpler than encapsulation. In fact, it is exactly the same as the encapsulation we first defined, but it is implemented in a different way.

// 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() --我是白猫

If you don’t understand it, it is recommended to relearn Table, metatable and object-oriented


Polymorphism

Polymorphism means that for the same method of a parent class, subclasses can execute different logic. What can we do to achieve polymorphism?

  1. Overriding and overloading methods
  2. implement interface
  3. Implement abstract classes and abstract methods

If rewritten it should be like this:

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

Although rewriting can be achieved, the problem is that rewriting after inheriting the parent class cannot retain the method of the same name of the parent class. So what should I do if I want to access the method of the parent class?

Don’t forget that our class is actually a table. Can’t I just store the parent class directly in it and then access it when I want to use it? There are no object-oriented syntax restrictions anyway.

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() --我是基类 我是狗?

Note that if we use the parent class method directly, we should avoid different classes sharing global variables when calling the parent class:

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
我是狗?

The reason is also very simple. tableThe parent class is stored in the memory table. If we call the method of the parent class directly Test, then self.ideach call will increase by one. The parent class in the two tablehas the same address, and this is what is Object:Test()passed in every time in this method , that is, the parent class itself, so what is added is in the parent class . As a global variable, it is naturally constant. Increased.selfxxx.basetableself.idid

So if we want the subclass to be able to Object:Test()rewrite based on inheriting the parent class method, and also want to self.idchange selfthe subclass table where we use the method, then we should write it like this:

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
我是猫类

Overloading should be the simplest polymorphic method. You only need to change the number of input parameters of the function.

As for interfaces and abstract classes, Lua's own functions can be rewritten, and the abstraction is still very strong. As for the interface, we should be able to access another table structure to implement it. For example, self.base should be regarded as an interface. Of course, these are just my thoughts and I have not learned it yet.

Guess you like

Origin blog.csdn.net/milu_ELK/article/details/131978479