lua代码规范

[TOC]

命名

  • 小写蛇形命名
    1. 文件和目录名
    2. 本地变量,全局变量g_开头
local my_var = 0
g_my_var = 0
  • 大写蛇形命名
    1. 全局常量,以G_开头
G_SERVER_ID = 1
  1. 全局枚举,以E_开头且_TYPE结尾
E_SERVER_TYPE = {
    LOGIN = 1,
    GAME = 2,
    GLOBAL = 3,
}

排版

  • 逗号之后添加一个空格
-- bad
local t = {1,2,3}

-- good
lcoal t = {1, 2, 3}
  • 运算符赋值符连接符前后各添加一个空格
-- bad
local sum = 1+2

-- good
local sum = 1 + 2
  • 如查不能突出前后对齐关系,则应该避免对齐声明
-- bad
local a        = 1
local var_name = 2

-- good
func(a,        b, c)
func(var_name, b, c)
  • 函数调用,不能省略括号
-- bad
func a, b, c

-- good
func(a, b, c)
  • 不同逻辑代码块之间要空行
-- bad
function remove(list, n)
    if n <= 0 then
          return 0
    end
    table.sort(list, function(a, b)
          return a.id < b.id
    end
    for i = 1, n do
         local sz = #list
         if sz  >  0 then
              table.remove(list, sz)    
         end
    end
    return 
end

-- good
function remove(list, n)
    if n <= 0 then
          return 
    end

    table.sort(list, function(a, b)
          return a.id < b.id
    end

    for i = 1, n do
         local sz = #list
         if sz  >  0 then
              table.remove(list, sz)    
         end
    end

end
  • 代码尽可能减少缩进 ```lua – bad if a ~= nil then if b ~= nil then – do someting end end

– good if a ~= nil and b ~= nil then – do someting end


# 设计

* 使用`_`表示未被使用的变量

```lua
-- good
local list = {1, 2, 3, ...}
for _, v in pairs(list) do
    print(v)
end

-- good
local _, n = string.gsub("aabbc", "a", "")
  • 私有对象使用_前缀命名
local M= {}
function M:init()
  self.a = 0

     -- good
  self._b = 0 -- 私有变量
end

-- good
function M:_func()
end

return M
  • 遍历配置表/数据库数据,v值尽量命名为row
-- good
for _, row in pairs(Config.ItemConfig) do
  ...
end

-- good
local row_list = db:find_all(...)
for _, row in pairs(row_list) do
  ...
end

  • 尽早验证并返回
-- bad
function enter_room(role, room_id)
     local cfg = Config.RoomConfig[room_id]
     if cfg ~= nil then
          local ok = role:cost_item(cfg.cost_item)
          if ok then
               ....
               return true
          else
               return false
          end
     else
          return fasle
     end

     return false
end

-- good
function enter_room(role, room_id)
     local cfg = Config.RoomConfig[room_id]
     if cfg == nil then
          return false
     end

     local ok = role:cost_item(cfg.cost_item)
     if not ok then
          return false
     end

     ....

     return true
end
  • 与业务处理有强相关的函数,尽可能返回错误码
-- bad
function can_send_chat(role)
     -- 角色禁言
     local is_ban = role:is_chat_ban()
     if is_ban then
          return false
     end

     -- 聊天CD时间
     local now = os.time()
     if role.chat_cd > 0 and role.chat_cd < now then
          return false
     end

     return true
end

-- good
function can_send_chat(role)
     -- 角色禁言
     local is_ban = role:is_chat_ban()
     if is_ban then
          return E_ERR_TYPE.CHAT_BAN
     end

     -- 聊天CD时间
     local now = os.time()
     if role.chat_cd > 0 and role.chat_cd < now then
          return E_ERR_TYPE.CHAT_CD
     end

     return E_ERR_TYPE.SUCCESS
end
  • 跨行定义table变量,需要在末尾添加,
-- bad
local t = {
     a = 1
}

-- good
local t = {
     a = 1,
}

-- bad
local t = {a = 1,}

-- good
local t = {a = 1}
  • 变量类型不要混合nil和false的含义
--bad
function can_get_mail(mail, role)
     local now = os.time()
     if now >= mail.expire_ts then
          return
     end
     
     if role.create_ts > mail.create_ts then
          return
     end

     return true
end

--good
function can_get_mail(mail, role)
     local now = os.time()
     if now >= mail.expire_ts then
          return false
     end
     
     if role.create_ts > mail.create_ts then
          return false
     end

     return true
end
  • 变量的作用域尽量小
-- bad
function upgrade_equip(role, equip_id)
     local equip_obj = role:get_equip(equip_id)

     local cfg = Config.EquipConfig[equip_id]
     if cfg == nil then
          return E_ERR_TYPE.CONIFG_ERR
     end

     return equip_obj:upgrade()
end

-- good
function upgrade_equip(role, equip_id)
     local cfg = Config.EquipConfig[equip_id]
     if cfg == nil then
          return E_ERR_TYPE.CONIFG_ERR
     end

     local equip_obj = role:get_equip(equip_id)
     return equip_obj:upgrade()
end
  • 变量的作用域在多个代码块,需要顶行声明且用空行分隔
-- bad
function buy_item(role, item_id, count)
     local shop_obj = role.shop
     local is_shelf = shop_obj:is_item_shelf(item_id)
     if not is_shelf then
          return E_ERR_TYPE.SHOP_ITEM_OFF_SHELVES
     end

     local remain_count = shop_obj:get_remain_count(item_id)
     if remain_count < count then
          return E_ERR_TYPE.SHOP_BUY_COUNT_LIMIT
     end
     
     ....
end

-- good
function buy_item(role, item_id, count)
     local shop_obj = role.shop

     local is_shelf = shop_obj:is_item_shelf(item_id)
     if not is_shelf then
          return E_ERR_TYPE.SHOP_ITEM_OFF_SHELVES
     end

     local remain_count = shop_obj:get_remain_count(item_id)
     if remain_count < count then
          return E_ERR_TYPE.SHOP_BUY_COUNT_LIMIT
     end
     
     ....
end
  • 非业务全局函数,文件名以变量名称定义,且方法以.声明
-- file: table.lua
function table.shuffle()
....
end

-- file: string.lua
function string.split()
....
end
  • 批量操作的代码顺序,要与代码声明顺序保持一致
local M = {}
function M:init()
     self.a = 0
     self.b = 0
     self.c = 0
end

-- bad
function M:destroy()
     self.b = 0
     self.a = 0
     self.c = 0
end

-- good
function M:copy(right)
     self.a = right.a
     self.b = right.b
     self.c = right.c
end

return M
  • 针对业务的全局函数
    1. 文件名以xxx_helper的格式定义
    2. 方法以:声明,用以区分非业务全局函数
    3. 为了减少全局变量污染,有需要使用的地方才会将xxx_helper对象require进来,所以xxx_helper需要在最后返回对象
-- file: mail_helper.lua
local M = {}

function M:send_mail(role_id, mail_id, ...)
end

-- 其它声明的函数
return M
  • 尽量不要在多层嵌套作用域中退出函数 ```lua function foo() if … then if … then – bad return true end end

    for _, i in pairs(list) do for _, j in pairs(batch) do if … then – bad return false end end end

    return true end

function foo() local result = false if … then if … then – good result = true end end

 for _, i in pairs(list) do
      for _, j in pairs(batch) do
           if ... then
                -- good
                result = false
                break
           end
      end
 end

 return result end

* 不要直接在事件通知函数,处理业务
```lua
-- bad
function M:on_xx_timer()
    -- do something here
end

-- good
function M:do_foo()
    -- do something here
end
function M:on_xx_timer()
     self:do_foo()
end

注释

  • 注释--后,需要空格
-- bad
local v = 1 --v is a number
-- good
local v = 1 -- v is a number
  • 文件头
--- summary.
-- Description; this can extend over
-- several lines
-- @script xxx.lua
-- @author xxx
  • 函数
--- split a string in two.
-- It is a specialized splitting operation on a string.
-- @param [type=string] s the string
-- @param [type=string] delim the delimiter (default space)
-- @return first part
-- @return second part
-- @usage local hello,world = split2("hello world")
funtion split(s, delim) .. end
....
end

@param 的参数类型

类型 type
整数 int
浮点数 float
字符串 string
函数 function
table
协程 coroutine
指针 userdata
  • 数组&哈希表
--bad
function M:init() 
     self.battle_map = {}
     self.battle_list = {} 
end
-- good
function M:init() 
     -- <battle_id:int, battle_tb:table> battle_tb结构: self:new_battle_struct()
     self.battle_map = {}
     -- {v1:table, v2:table, ...} 元素结构: self:new_battle_struct()
     self.battle_list = {} 
end

function M:new_battle_struct()
    return {
          ... 
     }
end

缩写

常见缩写https://lizijie.github.io/2018/04/23/%E5%B8%B8%E8%A7%81%E4%BB%A3%E7%A0%81%E8%8B%B1%E6%96%87%E8%AF%8D%E6%B1%87%E7%BC%A9%E5%86%99.html



原文:
https://lizijie.github.io/2022/06/05/lua%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83.html
作者github:
https://github.com/lizijie

PREVIOUS数值程序化 lua中的load运用
NEXT搭建centos基础开发环境的一些考虑