15.面向对象

15.面向对象


15.1 知识点

封装

  • Lua中的面向对象 类 其实都是基于表来实现 并且会结合元表相关的知识点

创建万物之父类 Object表

Object = {}

给万物之父手动添加实例化的new方法

-- 使用到的知识:在类外使用:在类外添加的成员方法 
--由于Lua中的类(表)是类似c#中的静态类 没有实例化方法 所以我们要给万物之父手动添加实例化方法
--万物之父设置实例化方法
function Object:new()
    --实例化方法的目的是返回一个新的对象 对象就是变量
    --Lua中的对象可以理解为返回一个表变量

    local obj = {}       --要实例化的对象 表变量 防止全局定义 要是局部变量

    setmetatable(obj, self) --设置万物之父是当前实例化对象的元表

    self.__index = self  --设置万物之父的__index表是万物之父自己 这样实例化对象找不到属性的时候可以去万物之父中找

    return obj           --返回对象
end

给万物之父设置一个属性id

Object.id = 1

给万物之父设置打印id属性的方法

function Object:PrintId()
    print(self.id)
end

实例化对象并使用

local myObj = Object:new() --实例化对象

print(myObj)               --table: 00BAA670 对象本质是表

print(myObj.id)            --1 myObj其实是没有id属性的 是去myObj的元表Object的_index指向的Object中找的id属性
--模拟对象实例化后属性没有设置默认值 那么就是类中属性自带的默认值 这个默认值是去元表元表Object的_index指向的Object中找

myObj:PrintId()            --1  myObj其实是没有PrintId方法的 是在PrintId方法中去myObj的元表Object的_index指向的Object中找的PrintId方法并调用
--模拟对象实例化调用类中封装的方法

myObj.id = 2               --给当前对象也设置一个属性叫id

print(Object.id)           --1 仍然是1 不会改动到Object

print(myObj.id)            --2 因为myObj设置了属性id 不会去找myObj的元表Object的_index指向的Object
--模拟对象实例化后属性设置了默认值 那就不去类中找默认值了 用自己赋值的

myObj:PrintId()            --2  myObj其实仍然是没有PrintId方法的 但是传入了myObj作为参数 在PrintId方法中 因为myObj设置了属性id可以直接输出
--PrintId方法仍然用类公有的PrintId方法 但是值已经是自己设置的了

继承

回顾_G表

--继承要用到_G表的知识 _G本质上是一个表 以键值对的形式存储了所以全局变量
print(_G) --table: 00D54F00  _G本质上是一个表

-- 设置全局变量 a 的初始值为 111
_G["a"] = 111
print(a) -- 输出 111

-- 修改全局变量 a 的值为 222
_G.a = 222
print(a) -- 输出 222

-- 尝试将全局变量 a 的值作为键,设置 _G 表中的键为 222 的值为 333
-- 注意:这不会改变全局变量 a 的值
_G[a] = 333
print(a)       -- 输出 222
print(_G[222]) -- 输出 333

-- 修改全局变量 a 的值为字符串 "a"
_G.a = "a"
print(a)     -- 输出 "a",全局变量 a 的值是字符串 "a"
print(_G[a]) -- 输出 "a",这样写等同于 _G["a"]

给万物之父设置继承的方法 要传入子类名 要双引号包裹

function Object:subClass(className)
    --继承的目的是设置传入表和调用继承方法表之前的继承关系 继承关系通过元表建立

    --把传进来的子类 在_G中为传入来的类名设置全局变量 初始为空表
    _G[className] = {}

    --使用元表建立继承规则的关联
    _G[className].base = self      --base代表父类属性 self代表Object 设置子类的父类属性为Object
    setmetatable(_G[className], self) --设置子类的元表是Object万物之父
    self.__index = self            --设置万物之父的__index表是万物之父自己 这样子类找不到的属性和方法可以去Object中找
end

子类继承万物之父Object

Object:subClass("Person")    --子类Person继承万物之父Object

print(Person)                --table: 00E5A4B8

local person1 = Person:new() --实例化Person
-- Person现在是空表 没有new方法
-- 去Person的元表Object的__index指向的表Object中找 发现有new方法
-- new方法会把Person作为第一个参数传进去 Person的__index设置成Person自己
-- 返回一个实例化Person的对象 并且元表是Person

print(person1.id) --1 person1现在是空表
-- 去元表Person的__index指向的表Person中发现也没有id 再上一层去Person的元表Object的__index指向的表Object中找 发现有id属性 返回id属性

person1.id = 100
print(person1.id)          --100 person1表有了id属性

person1:PrintId()          --100 person1没有:PrintId()方法 会一直找到Object发现有PrintId方法 传入自身person1作为参数 打印自身的id属性 刚刚设置了100


Object:subClass("Monster") --另一子类Monster和Person类似

local monster1 = Monster:new()

print(monster1.id)         --1

monster1.id = 200
print(monster1.id)         --200

monster1:PrintId()         --200

多态

相同行为 不同表现 就是多态
相同方法 不同执行逻辑 就是多态

创建继承万物之父的GameObject类

Object:subClass("GameObject")

--设置GameObject的坐标属性
GameObject.posX = 0;
GameObject.posY = 0;

--设置GameObject移动方法
function GameObject:Move()
    self.posX = self.posX + 1
    self.posY = self.posY + 1
    print(self.posX)
    print(self.posY)
end

创建继承GameObject类的Player类

GameObject:subClass("Player")

--设置Player的移动方法
function Player:Move()
    --Player继承GameObject 所以Player存在base属性 base是GameObject 表(类)

    --Player正常会要调用基类GameObject的移动方法

    --如果使用冒号调用GameObject的移动方法 会把GameObject作为第一个参数传入到移动方法中
    --这样实例化的任何Player对象 共用同一张GameObject表的属性 
    --有点修改的是静态变量的意思 而不是对象自身的变量
    -- self.base:Move()--存在问题


    --如果要执行父类的逻辑 我们不要直接使用冒号调用 要通过.调用 
    --然后传入自己作为第一个参数 
    --GameObject的移动方法中的self意味着传入方法中的第一个参数 
    --这样在GameObject的移动方法中的self会是实例化出来的player
    self.base.Move(self)

end

local player1 = Player:new()
player1:Move()--1 1

local player2 = Player:new()
player2:Move()--1 1 不会影响到player1

player1:Move()--2 2 

15.2 知识点代码

print("**********面向对象************")


print("**********知识点一 封装************")
--Lua中的面向对象 类 其实都是基于表来实现 并且会结合元表相关的知识点

--创建万物之父类
Object = {}

--由于Lua中的类(表)是类似c#中的静态类 没有实例化方法 所以我们要给万物之父手动添加实例化方法
--万物之父设置实例化方法
function Object:new()
    --实例化方法的目的是返回一个新的对象 对象就是变量
    --Lua中的对象可以理解为返回一个表变量

    local obj = {}       --要实例化的对象 表变量 防止全局定义 要是局部变量

    setmetatable(obj, self) --设置万物之父是当前实例化对象的元表

    self.__index = self  --设置万物之父的__index表是万物之父自己 这样实例化对象找不到属性的时候可以去万物之父中找

    return obj           --返回对象
end

--给万物之父设置一个属性id
Object.id = 1

--给万物之父设置打印id属性的方法
function Object:PrintId()
    print(self.id)
end

local myObj = Object:new() --实例化对象
print(myObj)               --table: 00BAA670 对象本质是表
print(myObj.id)            --1 myObj其实是没有id属性的 是去myObj的元表Object的_index指向的Object中找的id属性
--模拟对象实例化后属性没有设置默认值 那么就是类中属性自带的默认值 这个默认值是去元表元表Object的_index指向的Object中找
myObj:PrintId()            --1  myObj其实是没有PrintId方法的 是在PrintId方法中去myObj的元表Object的_index指向的Object中找的PrintId方法并调用
--模拟对象实例化调用类中封装的方法
myObj.id = 2               --给当前对象也设置一个属性叫id
print(Object.id)           --1 仍然是1 不会改动到Object
print(myObj.id)            --2 因为myObj设置了属性id 不会去找myObj的元表Object的_index指向的Object
--模拟对象实例化后属性设置了默认值 那就不去类中找默认值了 用自己赋值的
myObj:PrintId()            --2  myObj其实仍然是没有PrintId方法的 但是传入了myObj作为参数 在PrintId方法中 因为myObj设置了属性id可以直接输出
--PrintId方法仍然用类公有的PrintId方法 但是值已经是自己设置的了


print("***********知识点二 继承************")

--回顾_G表
--继承要用到_G表的知识 _G本质上是一个表 以键值对的形式存储了所以全局变量
print(_G) --table: 00D54F00  _G本质上是一个表
-- 设置全局变量 a 的初始值为 111
_G["a"] = 111
print(a) -- 输出 111
-- 修改全局变量 a 的值为 222
_G.a = 222
print(a) -- 输出 222
-- 尝试将全局变量 a 的值作为键,设置 _G 表中的键为 222 的值为 333
-- 注意:这不会改变全局变量 a 的值
_G[a] = 333
print(a)       -- 输出 222
print(_G[222]) -- 输出 333
-- 修改全局变量 a 的值为字符串 "a"
_G.a = "a"
print(a)     -- 输出 "a",全局变量 a 的值是字符串 "a"
print(_G[a]) -- 输出 "a",这样写等同于 _G["a"]

--给万物之父设置继承的方法 要传入子类名 要双引号包裹
function Object:subClass(className)
    --继承的目的是设置传入表和调用继承方法表之前的继承关系 继承关系通过元表建立

    --把传进来的子类 在_G中为传入来的类名设置全局变量 初始为空表
    _G[className] = {}

    --使用元表建立继承规则的关联
    _G[className].base = self      --base代表父类属性 self代表Object 设置子类的父类属性为Object
    setmetatable(_G[className], self) --设置子类的元表是Object万物之父
    self.__index = self            --设置万物之父的__index表是万物之父自己 这样子类找不到的属性和方法可以去Object中找
end

Object:subClass("Person")    --子类Person继承万物之父Object
print(Person)                --table: 00E5A4B8
local person1 = Person:new() --实例化Person
-- Person现在是空表 没有new方法
-- 去Person的元表Object的__index指向的表Object中找 发现有new方法
-- new方法会把Person作为第一个参数传进去 Person的__index设置成Person自己
-- 返回一个实例化Person的对象 并且元表是Person
print(person1.id) --1 person1现在是空表
-- 去元表Person的__index指向的表Person中发现也没有id 再上一层去Person的元表Object的__index指向的表Object中找 发现有id属性 返回id属性
person1.id = 100
print(person1.id)          --100 person1表有了id属性
person1:PrintId()          --100 person1没有:PrintId()方法 会一直找到Object发现有PrintId方法 传入自身person1作为参数 打印自身的id属性 刚刚设置了100

Object:subClass("Monster") --另一子类Monster和Person类似
local monster1 = Monster:new()
print(monster1.id)         --1
monster1.id = 200
print(monster1.id)         --200
monster1:PrintId()         --200


print("**********知识点三 多态************")
--相同行为 不同表现 就是多态
--相同方法 不同执行逻辑 就是多态

--创建继承万物之父的GameObject类
Object:subClass("GameObject")
--设置GameObject的坐标属性
GameObject.posX = 0;
GameObject.posY = 0;
--设置GameObject移动方法
function GameObject:Move()
    self.posX = self.posX + 1
    self.posY = self.posY + 1
    print(self.posX)
    print(self.posY)
end

--创建继承GameObject类的Player类
GameObject:subClass("Player")
--设置Player的移动方法
function Player:Move()
    --Player继承GameObject 所以Player存在base属性 base是GameObject 表(类)

    --Player正常会要调用基类GameObject的移动方法

    --如果使用冒号调用GameObject的移动方法 会把GameObject作为第一个参数传入到移动方法中
    --这样实例化的任何Player对象 共用同一张GameObject表的属性 
    --有点修改的是静态变量的意思 而不是对象自身的变量
    -- self.base:Move()--存在问题


    --如果要执行父类的逻辑 我们不要直接使用冒号调用 要通过.调用 
    --然后传入自己作为第一个参数 
    --GameObject的移动方法中的self意味着传入方法中的第一个参数 
    --这样在GameObject的移动方法中的self会是实例化出来的player
    self.base.Move(self)

end

local player1 = Player:new()
player1:Move()--1 1

local player2 = Player:new()
player2:Move()--1 1 不会影响到player1

player1:Move()--2 2 

15.3 练习题

用lua实现面向对象,定义动物类,动物类有名字和说话方法,定义具体实现的狗类和猫类

定义万物之父基类

Object = {}
setmetatable(Object, Object)
Object.__index = Object

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

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

function Object:toString()
    return tostring(self)
end

object1 = Object:new()
print(object1:toString())

定义动物类,重写new方法

Object:subClass("Animal")
function Animal:new(animalName)
    local obj = Animal.base.new(self)
    obj.animalName = animalName
    return obj
end
function Animal:Speak()
    print("动物"..self.animalName.."开始叫")
end

定义狗类,重写说话方法

Animal:subClass("Dog")
function Dog:Speak()
    self.base.Speak(self)
    print("狗"..self.animalName.."开始旺旺叫")
end

dog1 = Dog:new("Spike")
dog1:Speak()

定义猫类,重写说话方法

Animal:subClass("Cat")
function Cat:Speak()
    self.base.Speak(self)
    print("猫"..self.animalName.."开始喵喵叫")
end

cat1 = Cat:new("Tom")
cat1:Speak()

运行打印结果

table: 00C297C8
动物Spike开始叫
狗Spike开始旺旺叫
动物Tom开始叫
猫Tom开始喵喵叫


15.4 练习题代码

print("**********面向对象练习题************")

Object = {}
setmetatable(Object, Object)
Object.__index = Object

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

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

function Object:toString()
    return tostring(self)
end

object1 = Object:new()
print(object1:toString())



Object:subClass("Animal")
function Animal:new(animalName)
    local obj = Animal.base.new(self)
    obj.animalName = animalName
    return obj
end
function Animal:Speak()
    print("动物"..self.animalName.."开始叫")
end



Animal:subClass("Dog")
function Dog:Speak()
    self.base.Speak(self)
    print("狗"..self.animalName.."开始旺旺叫")
end

dog1 = Dog:new("Spike")
dog1:Speak()



Animal:subClass("Cat")
function Cat:Speak()
    self.base.Speak(self)
    print("猫"..self.animalName.."开始喵喵叫")
end

cat1 = Cat:new("Tom")
cat1:Speak()


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏