一、前言
首先要知道为什么要有代码规范呢?线代软件产业经过几十年的发展,一个软件由一个人完成已经很少见了,软件都是在相互合作中完成的。那么合作就意味着你需要“看同伴代码”,并发表意见看法。
试想如果你的队友接手到这样的代码,有何感想?
#include<stdio.h>
int v, i, j, k, l, s, a[99];
int main() { for(scanf("%d",&s); *a-s; v=a[j*=v]-a[i], k=i<s, j+= (v=j<s&&(!k&&!!printf(2+"\n\n%c"-(!l<<!j)," #Q"[l^v?( l^j)&1:2])&&++l||a[j]<s&&v&&v-i+j&&v+i-j&&v+i-j))&&!( l%=s),v||(i==j?a[i+=k]=0:++a[i])>=s*k&&++a[--i]) ; }
这是每年一次的国际模糊C代码大赛于1991年的“最佳小程序”,这个程序的功能是打印出八皇后问题的全部解决方案。我想一个正常人都会觉得这样的代码是不可读的。然而当你的同伴写出类似如此的代码,那么你只能带把40米的大砍刀了吧!
虽然计算机只关心编译生成的代码你的程序采用什么缩进风格,变量名有无统一等等与执行无关。但是在一个团队里面,代码规范却十分重要,甚至影响项目的成败。
代码规范分为两个部分:
1.代码风格规范
2.代码设计规范
二、代码风格规范
代码风格规范原则:简明易读,无二义性
源文件结构
一个源文件包含(按顺序地):
1、package语句
2、import语句
3、一个顶级类(只有一个)
1、package语句不换行(即package语句写在一行里)
2、 import不要使用通配符
即,不要出现类似这样的import语句:import java.util.*;
不要换行
import语句不换行,列限制(4.4节)并不适用于import语句。(每个import语句独立成行)
顺序和间距
import语句可分为以下几组,按照这个顺序,每组由一个空行分隔:
所有的静态导入独立成组
com.google imports(仅当这个源文件是在com.google包下)
第三方的包。每个顶级包为一组,字典序。例如:android, com, junit, org, sun
java imports
javax imports
3、 只有一个顶级类声明
每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。
例外:package-info.java,该文件中可没有package-info类。
缩进:我们不采用Tab键,而是手动输入4个空格。
虽然Tab键一般为4个空格键,但在很多的编辑器中都可以拓展Tab键为多个空格键。不采用Tab键理由是不同情况可能显示不同的长度,严重影响阅读体验。
行宽:限定为100个字符
以前一些规定的行宽为80个字符太小了,但也不宜过长,影响阅读质量,因此确定为100字符
括号:在复杂的表达式中,用括号清楚地表示逻辑优先级
使读者能够快速、清楚看出表达式的运算顺序
断行与空白行{}
if(condition) IsFool();
else IsNotFool();
有人喜欢以上的风格,因为这样可以省去几行,似乎显得代码很精简,看似很高效。但是非常不利于程序的调试,难以有效观察程序变量的值。因此需要断行。于是得到以下代码:
if(condition){
IsFool();
}
else {
IsNotFool();
}
这个改进确实不错,也很清晰美观。但是还不够,这样的程序在多层嵌套时就显得很无力,难以找到结构的对应关系。于是得到以下的程序:
if(condition)
{
IsFool();
}
else
{
IsNotFool();
}
这样的排版不仅美观,而且清晰,很容易找到结构的对应关系。所以我们确定为这样的模式:每个“{”“}”各占一行。
分行:多条语句不要放在同一行,如下:
a=b;b=c;
if(a == b) printf("%d\n",c);
命名:首要原则--见名知意。普通变量采用Camel法,并采用名词或者组合名词来命名。而类型、类、函数名采用Pascal法,并采用动词或者动宾的方式命名。宏则全部采用大写字母,采用名词或者组合名词来命名,多个词之间用下划线连接。例子如下:
变量(variables)采用Camel命名法。类中控件名称必须与xml布局id保持一致。用统一的量词通过在结尾处放置一个量词,就可创建更加统一的变量,它们更容易理解,也更容易搜索。例如,请使用strCustomerFirst和strCustomerLast,而不要使用strFirstCustomer和strLastCustomer。
量词列表 | 量词后缀说明 |
---|---|
First | 一组变量中的第一个 |
Last | 一组变量中的最后一个 |
Next | 一组变量中的下一个变量 |
Prev | 一组变量中的上一个 |
Cur | 一组变量中的当前变量 |
包(packages): 采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名为xx(可以是公司或则个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名
包名 | 此包中包含 |
---|---|
com.xx.应用名称缩写.activities | 页面用到的Activity(activities层级名用户界面层) |
com.xx.应用名称缩写.base | 页面中每个Activity类共享的可以写成一个i额BaseActivity类 (基础共享的类) |
com.xx.应用名称缩写.adapter } | 页面用到的Adapter类 (适配器的类) |
com.xx.应用名称缩写.tools | 此包中包含:公共工具方法类(tools模块名) |
com.xx.应用名称缩写.bean(或则 com.xx.应用名称缩写.unity ) | 此包中包含:元素类 |
com.xx.应用名称缩写.db | 数据库操作类 |
com.xx.应用名称缩写.view(或则 com.xx.应用名称缩写.ui ) | 自定义的View类等 |
com.xx.应用名称缩写.service | Service服务 |
com.xx.应用名称缩写.broadcast | Broadcast服务 |
类(classes):名词,采用Pascal命名法,尽量避免缩写,除非该缩写是众所周知的, 比如HTML,URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。
类 | 描述 | 例如 |
---|---|---|
activity 类 | Aty或者Activity为后缀标识 | 欢迎页面类WelcomeAty.或者WelcomeActivity |
Adapter类 | Adp或者Adapte 为后缀标识 | 新闻详情适配器NewtDetailAdp或则直接 NewDetailAdapter |
解析类 | Hlr为后缀标识 | 首页解析类HomePosterHlr |
公共方法类 | Tools或Manager为后缀标识 | 线程池管理:ThreadPoolManager 日志工具类:LogTools |
数据库类 | 以DBHelper后缀标识 | 新闻数据库:NewDBHelper |
Service类 | 以Service为后缀标识 | 时间服务TimeService |
BroadcastReceive类 | 以Broadcast为后缀标识 | 时间通知TimeBroadcast |
ContentProvider | 以Provider为后缀标识 | |
直接写的共享基础类 | 以Base开头 | BaseActivity,BaseFragment |
layout中的id命名
命名模式为:view缩写_模块名称_view的逻辑名称
控件 | 缩写 |
---|---|
LayoutView | lv |
RelativeView | rv |
TextView | tv |
Button | btn |
ImageButton | imgBtn |
ImageView | mgView 或则 iv |
CheckBox | chk |
RadioButton | rdoBtn |
analogClock | anaClk |
DigtalClock | dgtClk |
DatePicker | dtPk |
EditText | edtTxt |
TimePicker | tmPk |
toggleButton | tglBtn |
ProgressBar | proBar |
SeekBar | skBar |
AutoCompleteTextView | autoTxt |
ZoomControls | zmCtl |
VideoView | vdoVi |
WdbView | webVi |
RantingBar | ratBar |
Tab | tab |
Spinner | spn |
Chronometer | cmt |
ScollView | sclVi |
TextSwitch | txtSwt |
ImageSwitch | imgSwt |
listView | lVi 或则lv |
ExpandableList | epdLt |
MapView | mapVi |
注释:说明程序做什么,为什么这么做。但是不说明怎么做,因为程序本身就可以说明。尽量采用ASCII码字符,不采用中文或者其他字符,否则会极大影响程序的可移植性。
1、短注释采用 //
2、较长注释用 /* */
错误示例:
int sum = 0;
//i从0开始到9,循环10次,sum依次与i相加
for(int i = 0;i < 10;i++)
sum += i;
不应该采用中文,并且注释没有必要说明如何做,程序可以说明
正确示例:
int sum = 0;
//calculate the sum from 0 to 9
for(int i = 0;i < 10;i++)
sum += i;
复杂的注释应当放在函数头,很多函数头的注释用来说明参数的类型等。
程序注释必须正确,否则必没有注释更糟
注释不宜过长
三、代码设计规范
代码设计规范不仅是程序书写的格式问题,而且牵扯到程序设计、模块之间的关系,设计模式等等。因为我们设计出的程序可能将被许多人使用,并且需要不断调试程序,因此遵循以下规范是正确的选择。
函数:程序的功能绝大部分都由函数来完成。关于函数,最重要的原则就是“只做一件事,并且做好”。
错误处理:可能大家觉得主要功能设计完成之后,只需要花很少的一部分时间来给代码加上一些错误处理。而事实上则是相反的,错误处理往往要花上整个项目80%的时间。
参数处理:在debug版本,所有参数都要验证其正确性。正式版本,从外部传递来的参数要验证其正确性
断言:当你很确切某事会发生时,那么就可以断言!如下:
……
assert(p == NULL)
……
当你不确定某事是否发生时就需要修改相应的代码,如下:
……
p = AllocateNewSpace(); //could fail
if(p == NULL)
……