热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

编码规范——程序员的自我修养(代码样例为lua代码或c++代码)

--------------------------------------命名部分(适用于lua代码及c++代码)----------------------------------
------------------------------------
--命名部分(适用于lua代码及c++代码)
------------------------------------
--
命名规范(统一的命名规范有助于提高编码效率):
--
***************
代码命名规范:
1.全局变量所有单词所有字母均大写,字母间加下划线。如UI_CONST。
2.函数名及局部变量名首单词首字母小写,其后单词首字母大写,非首字母均小写,字母间无下划线。如importModules和tempPosition。
3.类名所有单词首字母大写,非首字母均小写,字母间无下划线。如ObjBoss。
4.类中成员变量的命名在局部变量命名规范基础上额外要求加单下划线如_uniqueObj和_cardId,对于那些希望仅内部访问的变量(私有变量)则加双下划线如__index。
5.代码缩进(每行起始缩进)统一使用一个tab,一个tab的长度设置为4个空格单位。


附加:为保持函数调用方式一致,建议所有类的成员函数定义时无论需不需要访问self均加上self(即定义时加上:语法糖).


***************
文件、文件夹命名规范(适用于代码文件、美术资源文件、配置资源文件):
1.文件命名为首单词首字母小写,其后单词首字母大写,非首字母均小写,字母间无下划线。如cdEffect.png和guanYu.png
2.文件夹命名为所有字母小写,字母间加下划线(这样可与文件命名区分开)。如res,sprite,src,naming_standard


***************
表格列名及Cocostudio中控件命名规范:
1.表格列名及Cocostudio中控件名导入到程序中后均作为变量,故命名规范参照代码中局部变量命名规范,即:
首单词首字母小写,其后单词首字母大写,非首字母均小写,字母间无下划线。如attcFreq和pauseBtn。


------------------------------------
--编码风格部分
------------------------------------
--
变量的申明或定义:
--
lua作为一门语法宽松的动态语言,其允许你在任何时候任何地点给table或者userdata添加变量。
但如果真这样作则将导致对象管理上的极大困难(尤其在初始化和清空的时刻),而且查找bug时
将会吃下这样做的苦果。因此对此部分做如下要求:


***************
类的任何非静态成员属性都应该在ctor()函数中被恰当的申明,不允许在ctor()外给类或者对象添加变量。

*因此如下是符合要求的:

----------------------------------------
--obj的动画管理器,提供对某个obj的诸动画资源进行管理,包括暂停、播放等其他的操作
ObjAnimMgr = class("ObjAnimMgr")

--------------------------------------------------public
function ObjAnimMgr:ctor()
--动画所属的父节点(obj)
self.__parentObj= nil
--cocos2d-x中动画的armature对象(如需要获得请通过getArmature())
self.__armature = nil
--cocos2d-x中动画的animation对象(get from armature)(如需要获得请通过getAnimation())
self.__animation = nil
--帧事件回调函数列表
self.__frameEventCallback = {}
--动作播放完回调函数列表
self.__animOverCallback = {}
end
*而如下的代码是不符合要求的:

----------------------------------------
--obj的动画管理器,提供对某个obj的诸动画资源进行管理,包括暂停、播放等其他的操作
ObjAnimMgr = class("ObjAnimMgr")

--------------------------------------------------public
function ObjAnimMgr:ctor()
end

function ObjAnimMgr:someFunc()
if nil == self.frameEventCallback then
self.frameEventCallback = {}
end

--do others
end
--
注释(必要的注释是你对项目组所应当负的责任):
--
必要的注释不可或缺,过度的注释则反过来降低了代码的清晰度。
自己看懂自己的代码一般没有多大问题,但当别人接手你的代码后,必要的注释能够帮助他迅速的理解并掌握你的代码,从而降低你离职给项目组带来的影响。
必要的注释能帮助同组同事更好的理解你负责的模块或功能,从而当他人的开发跨越了你负责的部分时减少因理解而带来的bug及其他隐患。
全组成员养成加注释的好习惯,能大大降低因员工离职而可能带来的他人接手其代码的障碍及隐患,同时提高组内横向需求开发时的效率及质量。


以下情况下建议注释:
***************
1.逻辑代码中的复杂深奥难懂,或者有特殊意义的部分,加上注释有助于他人对此部分的理解。


***************
2.逻辑代码中的承上启下,阶段分割处,加上注释有助于他人对整体过程的认识。
*因此如下代码是被推荐的:
--计时调度器更新函数
function ClientTimer:__updateScheduler()
--检查移除池并移除其中的scheduler
for k, v in pairs(self.__delayRemovePool) do
self.__schedulerTbl[v] = nil
end
self.__delayRemovePool = {}

--检查增加池并增加其中的scheduler
for k, v in pairs(self.__delayAddPool) do
local schedulerInfo = { isAlive = true,
elapsed = 0.0,
interval = v.interval,
func = v.func,
timerType = v.timerType,
paused = v.paused,
calledTime = 0,
removeFunc = v.removeFunc,}

self.__schedulerTbl[v.id] = schedulerInfo
end
self.__delayAddPool = {}

--scheduler tick
for schedulerId, schedulerInfo in pairs(self.__schedulerTbl) do
if (not schedulerInfo.paused) and schedulerInfo.isAlive then
--未被暂停,需要tick
local dt = self:getDeltaSec(schedulerInfo.timerType)
schedulerInfo.elapsed = schedulerInfo.elapsed + dt
if schedulerInfo.elapsed > schedulerInfo.interval then
--到了触发时间,触发
schedulerInfo.elapsed = 0.0
--调用次数累加
schedulerInfo.calledTime = schedulerInfo.calledTime + 1
schedulerInfo.func(dt)
end
end
end
end
***************
3.变量在申明时,申明变量的时候添加注释表明其用途和一些注意事项,之后变量使用过程中不必做注释。
*因此如下代码是被推荐的:
function ObjAnimMgr:ctor()
--动画所属的父节点(obj)
self.__parentObj= nil
--cocos2d-x中动画的armature对象(如需要获得请通过getArmature())
self.__armature = nil
--cocos2d-x中动画的animation对象(get from armature)(如需要获得请通过getAnimation())
self.__animation = nil
--帧事件回调函数列表
self.__frameEventCallback = {}
--动作播放完回调函数列表
self.__animOverCallback = {}
end


***************
4.接口提供处,在提供接口时加入充分必要的注释,能使他人一眼就明白该接口的作用和使用方法从而降低错误使用接口的可能。
接口处的注释不够丰富或明确将大大提高模块的使用难度,使得调用者需要深入了解模块的实现才能掌握模块的使用方法。
接口处的注释方式视接口的功能和参数复杂度而定。
*如简单的接口可使用一句话作为注释,如:
--全局暂停,将暂停整个时间管理器(而不是单个时间线),用于暂停按钮
function ClientTimer:globalPause()
self.__lastPauseBeginTime = socket.gettime()
self.__paused= true
cc.Director:getInstance():pause()
end
*而复杂的接口(比如功能复杂或者参数较多或行为有一定的不确定性)则需要更详细的说明,如:
---------------------------------------------
--公有方法
--功能:创建计时调度器
--参数func:计时回调函数
--参数intervalTime:计时时长
--参数tag:调度器所属的时间线
--参数removeFunc:该计时器被移除时的回调函数(有则调用),调用时参数为被调度器总共被调用的次数
--返回:创建的调度器id
---------------------------------------------
function ClientTimer:createScheduler(func, intervalTime, tag, removeFunc)
assert(tag, "tag cannot be nil")
self.__schedulerCnt = self.__schedulerCnt + 1
table.insert(self.__delayAddPool, {id = self.__schedulerCnt,
func = func,
interval = intervalTime,
timerType = tag,
paused = false,
removeFunc = removeFunc,})

return self.__schedulerCnt
end
5.模块文件开头处,用以标示该本文件的内容(如模块的功能)、修改信息等。此部分注释能帮助他人了解到该模块的功能及维护者和版本更迭过程,从而减少一些沟通成本。
该条仅针对那些功能相对较独立(不会被频繁修改)的模块文件。
*因此如下代码是被推荐的:

--Obj暂停管理器,考虑到本游戏需要大量暂停功能,故以管理器整合此部分代码

ObjPauseManager = class("ObjPauseManager")
--others
--
‘封装(提高封装能力,是个人能力进阶的必经之路):
--
"最小暴露原则":只要是对外界无用的变量(属性或方法),或是不期望被外界直接获取的变量,都应该像宝贝一样隐藏起来——即能不暴露的,就不暴露。
隐藏方法:
对于类的属性或方法,c++中通过访问控制属性private来实现隐藏,lua中可以使用明确的双下划线__来表示该变量是私有变量,外界不应该直接引用。
*因此如下代码是被推荐的:
--该属性为私有属性,外界切勿直接调用
self.__parentObj= nil
--该方法为私有方法,外界切勿直接调用
function ObjAnimMgr:__onAnimationEvent(armature, movementType, movementId)
--todo
end
非类的属性或方法,可用 local 关键字来表明其为本地属性,从而限制外界的访问。
*因此如下代码是被推荐的:
--该方法为本地方法,外界无法直接调用
local function importModules()
--todo
end
封装指的是模块实现逻辑透明,只对外提供清晰确定的接口,外界不需要也不应该接触到模块内部的具体实现界只需要借助接口来完成功能调用。
良好的封装性将带来如下好处:
***************
1.模块内部发生变化时由于接口保持不变,因而外部代码不需为此做出任何改变。


***************
2.模块的实现代码全局封装在模块内部,因此模块若存在bug也不会延伸到模块外去。


***************
3.更换模块非常容易,新旧模块只需保持接口一致即可(管它内部如何实现)。同样,模块的可移植性也很强。


--
清晰度&耦合度&内聚度(清晰度位居对代码所有考核指标之首,也是其他指标的前提):
--
清晰度不是外观,其重要性应据对位于所有的考评指标之首(包括性能、安全性、防错容错性、可扩展性、可移植性、可重用性、可维护性)。
清晰度是其他指标的前提,没有清晰度其他的指标无从谈起。
1.没有清晰度,意味着程序员无法对程序的执行细节一眼望穿,具体的执行逻辑将如一张巨大的渔网一样纠缠不清。bug越修越多(修一个引发三个,修完这三个又各引发三个,这绝对不是危言耸听)。
2.没有清晰度,常意味着代码很难理解且牵扯很强,正常的流程中存在着复杂的跳转和对外部的依赖,因此耦合度必然很高内聚性必然很低。
3.没有清晰度,扩展一个功能时会发现此部分对一大堆外界存在依赖且流程错综复杂,扩展根本无从下手,因为你动弹不得。
4.清晰度不高,则容错防错无从谈起。代码的正常行为本身就不够明确,如何防范正常行为以外的行为?
代码的主要执行逻辑应该是一条确定的大道,不应该有过多的岔路(条件跳转),绝对拒绝山路十八弯的逻辑。岔路一多,复杂性曾指数增长,其变化的可能远远超出人的思维负载从而突破程序员的掌控,你会被层出不穷的bug所困扰。


耦合:简单来说就是模块内部的行为对外部有依赖,此为耦合。
根据经验,如下几种方式能降低耦合:
***************
1.单入口单出口,任何模块或子系统维护一个统一的入口及出口,再多的需求都应该经由这个入口驱动模块。
单入口不是说某功能就仅只能有一个接口,接口可能有多个,但最终的真实入口应只有一个,如clientTime.lua中,创建scheduler的接口有多个,但最终都导向了唯一的入口createScheduler()
*因此如下代码是被推荐的:
--真实的入口
function ClientTimer:createScheduler(func, intervalTime, tag, removeFunc)
--some code
end
--本函数调用createScheduler实现
function ClientTimer:createSchedulerOnce(func, intervalTime, tag, removeFunc)
--some code
schedulerId = self:createScheduler(cbFunc, intervalTime, tag, removeFunc)
--some code
end
--本函数调用createScheduler实现
function ClientTimer:createObjScheduler(obj, func, intervalTime, removeFunc)
--some code
local schedulerId = self:createScheduler(func, intervalTime, obj:getTimerType(), removeFunc)
--some code
end
--...
--更多
--...

***************
2.函数的行为应该简单而确定,拒绝功能强大能做这个又能做那个的函数。如果一个函数能根据参数或者外部变量的值而做出不同的行为,你应该考虑把此函数拆分或者换一种新的思路。


***************
3.入口参数一旦确定,函数或模块的内部行为应该是确定的。反之你可能需要思考一下新的实现思路。
*如下的代码是不符合要求的:
function moduler:sampleFunc(val)
--upval1为moduler外部变量
if upval1 then
--do something
else
--do some other
end
end


***************
4.当发现有散布在多处的逻辑代码经过一定的调用顺序最后完成了一个较常用到的功能,你可能需要考虑是否应该将这些代码抽象出并形成一个模块(管理器)。 


--
防错容错(容错最重要的一点是:确定哪些该容哪些不改容):
--
一定的防错容错提升程序的健壮性,过多的防错容错拖累程序的性能并降低可读性。
***************
1.防错应该从源头上防(即在逻辑最起点检测),不要把错误检测留到过程中去反复检测(没必要且拖累性能)。
*因此如下代码是被推荐的:
--c++代码,摘自天龙服务端
BOOLLogin::NewLogin()
{
__ENTER_FUNCTION

g_pDataBaseManager= newLoginDBManager();
AssertEx(g_pDataBaseManager,"分配数据库管理器失败!");
Log::SaveLog(LOGIN_LOGFILE,"new LoginDBManager ...OK");

g_pProcessManager= new ProcessManager();
AssertEx( g_pProcessManager,"分配g_pProcessManager 失败!");
Log::SaveLog( LOGIN_LOGFILE, "new ProcessManager...OK" ) ;

g_pPlayerPool=new PlayerPool ;
AssertEx( g_pPlayerPool,"分配g_pPlayerPool 失败!");
Log::SaveLog( LOGIN_LOGFILE, "new PlayerPool...OK" ) ;

g_pPacketFactoryManager = new PacketFactoryManager ;
AssertEx( g_pPacketFactoryManager,"分配g_pFacketFactoryManager 失败!") ;
Log::SaveLog( LOGIN_LOGFILE, "new PacketFactoryManager...OK" ) ;

g_pProcessPlayerQueue= new TurnPlayerQueue;
AssertEx(g_pProcessPlayerQueue,"分配排队队列失败!");
Log::SaveLog(LOGIN_LOGFILE,"new g_pProcessPlayerQueue...OK");

//
//...
//
}
***************
2.仅容下那些由不确定因素而导致的错误(且容下这部分错误后程序还能基本正常运作),确定因素导致的错误应该果断报错并终止执行(从而快速的改正错误)(如果这种错误也容,那么你的程序将被容错代码所淹没)。 
因此对于全局管理器或者程序核心对象创建时的错误,绝对不应该容下,而应该直接报错并终止运行。(容下这些错误,程序还能运行吗,既然不能运行,容下它们有何用?)


***************
3.仅在过程中容错,启动时的错误(哪怕它再小)应该毫不留情的暴出。晚一点出门永远胜过带病出门。
因此举例而言,对于配置错误(如填表错误或者缺少资源等)哪怕再小,都不应该容。带病上阵除了会带来彻底的失败外,什么也不会带给你。


***************
4.容下错误,绝对不是隐瞒错误!因此容错时一定要配合给出足够明显的警告信息,如断言 assert(该错误十分严重)或显眼的log(该错误较轻微,尽管它发生了可程序也能继续正常运行)。
*因此如下代码是被推荐的:
--c++代码,摘自天龙服务端
LoginPlayer*pLoginPlayer = (LoginPlayer*)pPlayer ;
if( pLoginPlayer==NULL )
{
--报错
Assert(FALSE) ;
return PACKET_EXE_CONTINUE ;
}

*因此如下代码是被推荐的:
--时间线是否处于被暂停状态
function ClientTimer:isPauseTime(timerType)
if not self.__timerTbl[timerType] then
logger:warn("check pause no exist time, timerType = %s !!!", tostring(timerType))
return
end
--...
end
*如下的代码是不符合要求的:
--获得delta,其实对于所有计时器其每次delta都是一样的,这里传入计时器名只是判断其有未被暂停
function ClientTimer:getDeltaSec(timerType)
if not self.__timerTbl[timerType] then
--容下错误时未给出任何提示,将导致行为异常且找不到任何原因(即埋下了一个很深且难以被查找到的bug)
return
end
--...
end
***************
5.(该点存在争议)如果是一些若存在则调试中一定会发现且发现后可以很容易被修改且修改后基本就不会再发生的错误————如参数错误(明显是代码编写导致的错误),可以考虑仅防错而不容错(使用断言 assert ),从而尽量不拖累性能。该点存在一定争议,争议之处在于你很难完全确定这些前提条件是否成立。
*因此如下代码是被推荐的:
function ObjAnimMgr:create(parent, armature)
--本函数一定会在调试中被调用,且参数出错明显是代码编写错误
assert(parent and armature, "parent or armature in ObjAnimMgr:create() is nil!")

local mgr = self:new()
return mgr
end


------------------------------------
--全局
------------------------------------
***************
代码应该赏心悦目,每句话都恰到好处,多之显胖,少之显瘦。

***************
一个模块(工程),应该是一个被精雕细琢的艺术品,出自程序员这个艺术家之手。

***************
不要步入认识的误区:“代码越复杂人越牛逼”。越是新手越容易写出晦涩难懂的代码,高手的代码往往简洁清晰,且这样的代码常常更高效。

***************
永远不要试图用逻辑的方式解决架构的问题,否则你的程序将很快走到全面崩塌的边缘。

***************
动手开发每一个功能单之前应该问自己一个问题:“我是应该是写几句代码,还是几个函数,又或者是一个类,还是一个模块,还是一个工程?”


------------------------------------
--结束
------------------------------------
水平尚浅难免有疏漏或错误之处,若发现上述内容有任何问题,极其欢迎批评指正。






推荐阅读
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
author-avatar
mobiledu2502909113
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有