软件开发统一规范¶
规范目的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_1
test_3
getBall_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#
代码规范手册》 ↩