软件开发统一规范¶
规范目的1¶
作为一名合格的开发者,最基本的素质就是要做到编码规范,从小我们就接受教导 “字如其人”,而写代码亦是如此,良好的代码风格,彰显了个人的工作素养。而良好的代码规范,能够帮助我们进行更好的团队协作,它能方便代码的交流和维护;不会影响编码的效率,不与大众习惯冲突;使代码更美观、阅读更方便;使代码的逻辑更清晰、更易于理解。
一个软件的生命周期中,80%的花费在于维护;几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护。
好的编码约定可使源代码严谨、可读性强且意义清楚,与其它语言约定相一致,并且尽可能的直观。
高质量的代码往往具有如下特质:
-
易懂性 —— 代码必须易读且简单明确的。它们必须能展示出重点所在,且代码应该做到易于重用,不可包含多余代码,它们必须带有相应文档说明。
-
正确性 —— 代码必须正确展示出其要告知使用者的重点。代码必须经过测试,且可以按照文档描述进行编译和正确运行。
-
一致性 —— 代码应该按照一致的编程风格和设计来保证代码易读。 同样的,不同代码之间也应当保持一致的风格和设计,让使用者能够很轻松的结合使用它们。一致性能将我们代码库优良的品质形象传递给使用者,展示出我们对于细节的追求。
-
流行性 —— 代码应当展示现行的编程实践,例如使用 Unicode,错误处理,防御式编程以及可移植性。代码应当使用当下推荐的运行时库和 API 函数,以及推荐的项目和生成设置。
-
可靠性 —— 代码必须符合当地法律,隐私和政策标准和规范。不允许展示入侵性或低质的编程实践,不允许永久改变机器状态。所有的安装和执行过程必须是可以被撤销 的。
-
安全性 —— 代码应该展示如何使用安全的编程实践 :例如最低权限原则,使用运行时库函数的安全版本,以及 SDL 推荐的项目设置。
命名规范¶
基本命名原则¶
- 一定要为各种类型,函数,变量,特性和数据结构选取有意义的命名。其命名应该能直接反映其作用。
所谓 自注释 的代码就是好代码。避免容易被主观解释的难懂的名称,如 AnalyzeThis(),或者属性名 Temp。这样的名称会导致多义性。
在类属性的名称中包含类名是多余的,如 User.UserName。而是应该使用 User.Name, “.”即中文的“的”的意思。
- 名称应该 说明“什么”而不是“如何” 。
通过避免使用公开基础实现(它们会发生改变)的名称,可以保留简化复杂性的抽象层。例如,可以使用 GetNextStudent(),而不是 GetNextArrayElement()
- 不应该在标识符名中使用不常见的或有歧义的缩短或缩略形式的词。
比如,使用 GetTemperature 而不是 GetTemp,Temp 到底是 Temperature 的缩写还是 Temporary 的缩写呢。对于公共类型或大家都知道的缩写,则可以使用缩略词,如:线程过程,窗口过程,和对话框过程函数,为 ThreadProc, DialogProc, WndProc 等使用公共后缀。
- 不要使用计算机领域中未被普遍接受的缩写。
在适当的时候,使用众所周知的缩写替换冗长的词组名称。例如,用 UI 作为 User Interface 缩 写,用 OLAP 作为 On-line Analytical Processing 的缩写。
- 命名严禁使用拼音与英文混合的方式,更不允许直接使用中文。
正确的英语拼写和语法可以让阅读者易于理解,避免歧义。注意:即使纯拼音命名的方式也要避免采用。正例:name / order/ baidu / alibaba 等国际通用的名称可视为英文。 反例:zhekou(折扣)/Shuliang(数量)/int 变量=1
- 单词力求语义表达要完整,不要嫌名字长。
正例:MaxStockCount 反例:Max_Count;反例 2:int a 的随意命名方式(除非极少数简单情况可以使用简单变量如int i作为索引。
- 抽象类命名推荐使用
Base结尾,异常类命名使用Exception结尾,测试类命名以它要测试的类的名称开始,以Test结尾。杜绝不规范的缩写,避免望文不知义。例:NotFoundException
命名样式¶
| 名称 | 使用范围 | 示例 | 备注 |
|---|---|---|---|
Pascal 命名法/ UpperCamelCase |
类名,结构体名,namespace | MyClass MyStruct |
现代,主流流行。对应实例化的类对象使用 lowerCamelCase |
驼峰命名法/ lowerCamelCase |
函数名,局部变量名 | getBall position |
现代,主流流行 |
SCREAMING_SNAKE |
head file | #include CICLE_H |
使用不多。在 MSVC 中直接使用 #pragma once 即可。 |
| 【禁用】 | gPlay mPosX |
使用 g 或者 m 等前缀标识变量的作用域,在现代智能感知编辑器中属于**极其累赘**的行为,不仅 m g 意义不明,同时输入累赘 |
|
lower_camel_case |
【慎用】 | get_ball |
带有下划线的在输入过程中多打 _ 需要多按两个键,极度影响输入效率,完全就是累赘。 |
lower_camel_case 的使用场景
lower_camel_case 只有少数情况下可以使用。
- 某些临时测试过程中的后缀。比如
ballPosition_1test_3getBall_test3 - 某些语义相差甚远的单词。比如
getBall_test,test不属于getBall的一部分,临时用于自我标记。但是在对外接口命名设计上,要将过程测试的名称删除或者重新改回getBall - 以及某些
lowerCamelCase不能标识的单词。比如getBall_SRC等
本项目所使用的命名样式对应表¶
| 标识符 | 规范 | 命名结构 | 示例 |
|---|---|---|---|
| 文件名 | Pascal 或者 camel | 建议对于大型的、封装的、接口的文件采用 Pascal 命名法;对于局部的、小型的、具体实现的文件采用 camel 命名法 | |
| 类名、结构体 | Pascal 命名法 | 名词 | class Point{...}; |
| namespace | Pascal 命名法 | 名词 | |
| 枚举 | Pascal 命名法 | 名词 | |
| 函数 | camel 命名法 | 动词或动词短语 返回 bool 类型的函数应当含有 can has is 等单词 |
void getBall(){...}bool canChipPassTo(){...} |
| 变量/字段 | camel 命名法 | 名词 其中 bool 类型的变量应当含有 is 或者 has |
int ballPosition;bool isBubbled; |
| 常量 | camel 命名法(同变量) 或者全大写 |
名词 | |
| 属性 | Pascal 命名法 | 名词 | public: int Name; |
| 方法 | Pascal 命名法 | 动词或动词短语 | public: void GetBall(){...} |
注意
1.变量名命名补充规则
只要合适,在变量名的末尾或开头加计算限定符(Avg、Sum、Min、Max、Index)。在变量名中使用互补对,如 min/max、begin/end 和 open/close。
布尔变量名通常应该包含 is或者has,这意味着 True/False 值,如 isFileFound,若单词的意义本身已经包含是非的情况,可省略 is,如 exist。
在命名状态变量时,避免使用诸如 Flag 的抽象泛用名词。状态变量不同于布尔变量的地方是它可以具有两个以上的可能值。比如订单状态不应该是 OrderFlag,而是使用更具描述性的名称,OrderStatus。
即使对于可能仅出现在几个代码行中的生存期很短的变量,仍然需要使用有意义的名称。仅对于短循环索引使用单字母变量名,如 i 或 j。
只有临时变量和仅供自己测试使用的变量名称末尾可以使用下划线_,加上诸如test v1之类的后缀
2.属性和方法分别是对字段/函数的进一步封装。在 C++ 语法中并无对应。但是在某些情况下认为是属性/方法。比如
分别使用 Age() 函数对 age 字段进行了封装。int& Age() 是可读可写的属性,const int& Age() const; 是只读属性,供 const 对象调用。
属性的实现方法还有很多,以下是另一种常见的属性实现:
其中 GetAge() 和 SetAge() 函数同样是对 age 这一私有字段的封装。同学们在编程实践中肯定遇到很多这类情况。命名推荐是使用 Pascal 命名法以同私有字段区别。
方法在 C++ 中的实现:
其中 eat(int agrs[]) 是内部实现,涉及到很多传参,甚至有可能递归调用。Eat() 是供外部调用的接口。为以示区别,同样应当采用 Pascal 命名法
项目已有命名现象举例¶
| 名称 | 说明 | 应当修改成 |
|---|---|---|
goCmuRush |
Cmu 意义不明。放在 go 这样的 task 中毫无意义,任务本身不需要使用 Cmu 说明。 |
Rush 或者 rush |
getball() |
应当使用 camel 或者 Pascal 命名法。无论如何都是 getBall() 或者 GetBall() 单词之间由大写字母分隔。犯此类错误是对命名规则的不了解 |
getBall() 或者 GetBall() |
bufcnt |
意义不明,难以记忆。 | complexSwitch isLongEnough hasLastedFrames(帧数) 等 |
posX |
多处命名不统一并且混乱。在调用时容易和 ball.x() ball.X() ball.posx() ball.posX() ball.velX() ball.velPosX() 混淆 |
统一各处命名如 ball.x()。或者采用 ball.position().x() 等更近一层封装的方式解决。position 没必要缩写成 pos。鉴于项目各处已经广泛使用 pos 则保持 pos 统一即可。 |
gPlay |
混乱地采用的匈牙利命名法。 | GlobalPlayStart 或者 GlobalPlayEntrance 或者 GlobalPlaySetting |
MATCH.lua |
混乱的命名 | Match.lua |
NormalKick_th |
正式文件接口不建议使用意义不明的人名、拼音 | NormalKick 或者 NormalKick_test1,把 TaoHan 写在文件开头的注释中 |
代码样式规范¶
注释规范¶
代码的可读性优于注释, 并不是注释越多越好
- 文件开头应当表明由谁开发(这样便于维护时找到负责人),开发时间[可选],本文件主要内容等
- 对外的接口函数应当写明函数内容,参数意义等。可以在
Visual Studio编辑器中在函数前面敲入///则会自动生成带xml样式的长注释 - 通过代码无法表达的东西,如复杂的逻辑, 用法, TODO等,都可以通过注释的方式表达! asciiflow
杂项¶
-
使用C++智能指针并避免使用原始指针。
-
避免在同一行初始化多个变量。
-
避免使用三元运算符。清晰性比行数更重要。
-
总是在代码块周围使用花括号,即使花括号只包围一个语句。
-
避免直接使用数字,除非它是数学或物理公式的一部分(例如
A=0.5(b*h))。 -
复杂的if逻辑判断应该用函数封装起来,并使用有意义的函数名。
Git提交规范¶
关键¶
- 不同的事情尽量不要混在一次commit里面
- 讲清楚改了什么
- 不要commit一些与改动无关,自动改变的,如owl.ini
格式¶
包括三个字段:type(必需)、scope(可选)和subject(必需)。
type
type用于说明 commit 的类别,只允许使用下面7个标识。
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
如果type为feat和fix,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。
scope
scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
subject
subject是 commit 目的的简短描述,不超过50个字符。
- 以动词开头,使用第一人称现在时,比如
change,而不是changed或changes- 第一个字母小写
- 结尾不加句号(
.)
-
来自网络文件《
C#代码规范手册》 ↩