编码规范约定(初稿)
标签: Java
此规范是前公司写的一份约定初稿,这里我只是简单的记录一下,有时间再对其进行修改和完善。
[TOC]
1. 注释
1.1 代码注释
每个类和自建的public方法必须包含Javadoc注释,注释至少要包含描述该类或方法用途的语句,并且该语句应该用第三人称的动词形式来开头。
例如:
/**
* Tools for doing something.
*/
public class SomeTools {
}
/**
* Returns true if the string is null or 0-length.
*/
public static boolean isEmpty(CharSequence str) {
}
对于那些无关紧要的类似setFoo()的get和set语句是不必撰写注释;如果方法执行了比较复杂的操作(比如执行强制约束或者产生很重要的副作用),那就必须进行注释。如果“Foo”属性的意义不容易理解,也应该进行注释。
1.2 使用TODO注释
对那些临时性的、短期的、够棒但不完美的代码,请使用TODO注释;TODO注释应该包含全部大写的TODO,后跟一个冒号,如:
// TODO: Change this to use a flag instead of a constant.
此外,如果TODO注释是“将来要做某事”的格式,则请确保包含一个很明确的日期(“在2005年11月会修正”),或是一个很明确的事件(如:“在所有代码整合人员理解了V7协议之后删除本段代码”)。
下面以一段因产品临时需求变更但又 有可能随时回滚 的场景为例:
Bad
//void doChangedSomething(){
// ...
//}
Good
//TODO: 由于产品需求变更:去除逻辑XXX(之前的逻辑描述),将“这版本 5.0 正式发布后删除此代码”
//void doChangedSomething(){
// ...
//}
1.3 代码注释注意事项
- 注释应该增加代码的清晰度
- 保持注释的简洁
- 在写代码之前写注释
- 注释出为什么做了一些事,而不仅仅是做了什么
- 文档注释:在紧靠接口、类、成员函数和字段声明的前面注释它们。
- 在成员函数内采用单行注释,来说明业务逻辑、代码段和暂时变量的声明。注释符"//"后必须紧跟一个空格,然后才是注释信息。
- 注释哪些部分:
- 类。类的目的、即类所完成的功能,注释出采用的变量。
- 接口。 设置接口的目的、它应如何被使用以及如何不被使用。
- 成员函数注释。对于设置与获取成员函数,在成员变量已有说明的情况下,可以不加注释;普通成员函数要求说明完成什么功能,参数含义是什么返回什么。
- 普通成员函数内部注释。 控制结构,代码做了些什么以及为什么这样做,处理顺序等。
- 实参/参数。 参数含义、及其它任何约束或前提条件。
- 字段/属性。 字段描述。
- 局部变量。 无特别意义的情况下不加注释。
2. Java 代码编写约定
2.1 遵守字段命名惯例
非public的、非static的字段名称以m开头
非final 的static字段名称以s开头
其它字段以小写字母开头
-
public static final 字段(常量)全部字母大写并用下划线分隔
Bad
class CodeStyle{ private int count; private static int NextAccessibilityViewId; public static final int drawing_cache_quality_high = 0x00100000; public void doSomething(String mParams){ ... } }
Good
class CodeStyle{ private int mCount; private static int sNextAccessibilityViewId; public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000; public void doSomething(String params){ ... } }
2.2 尽量采用英语语义来声明类,变量及方法名
尽可能采用英语的语义拼写来命名并尽可能符合如下规则:
使用完整描述符来准确地描述变量、字段或类。
使用适用于领域内的术语。如果您的用户将他们的客户称为顾客,则对这个类使用术语Customer来代替Client。
避免长型名称,一般不要超过15个字符。
避免使用下划线。
类的普通方法一般采用完整的英文描述说明成员方法功能,第一个单词应是采用一个生动的 动词,并且首字母小写,其它每个单词首字母大写。例如 openFile(), addAccount()。
类的获取方法(一般具有返回值)一般要求被方法名使用被访问字段名,前面加上前缀get,例如getFirstName(), getLastName()。
类的布尔型的判断方法一般要求方法名使用单词 is 做前缀,如isPersistent(),isString()。或者使用具有逻辑意义的单词,例如equal 或equals。
类的设置方法(一般返回类型为void):被访问字段名的前面加上前缀 set,例如setFirstName(),setLastName(),setWarpSpeed()。
-
构造方法应该用递增的方式写(比如:参数多的写在后面)。例如:
public CounterSet(){ } public CounterSet(int size){ this.size = size; }
2.3 编写简短的方法
为了把规模控制在合理范围内,方法应该保持简短和重点突出。不过,有时较长的方法也是合适的,所以对方法的代码长度并 没有硬性的限制 。如果方法代码超过了40行,就该考虑是否可以 在不损害程序结构的前提下 进行分拆。
2.4 在标准的位置定义成员变量和方法
-
重载永不分离
当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法Bad
public class LinearLayout extends ViewGroup { public LinearLayout(Context context) { super(context); } public void doSomething(){ ... } public LinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LinearLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); ... } }
Good
public class LinearLayout extends ViewGroup { public LinearLayout(Context context) { super(context); } public LinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LinearLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); ... } public void doSomething(){ ... } }
-
以逻辑分块排列每个类的成员
每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的;
再比如activity生命周期的成员方法应集中放置并放置在类的前方;在功能上存在逻辑关联的变量应集中放置;在功能上存在逻辑关联的方法应集中放置;该类实现的接口或重载的父方法应该集中放置并放置在类的前方。Bad
public class BaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public void doSomething(){ } @Override protected void onRestart() { super.onRestart(); } public void doOtherSomething(){ } @Override protected void onPostResume() { super.onPostResume(); } public static Intent newInstance(Bundle paramBundle){ Intent intent = new Intent(); // doSomething return intent; } @Override public CharSequence onCreateDescription() { return super.onCreateDescription(); } @Override protected void onDestroy() { super.onDestroy(); } }
Good
public class BaseActivity extends Activity { public static Intent newInstance(Bundle paramBundle){ Intent intent = new Intent(); // doSomething return intent; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onRestart() { super.onRestart(); } @Override protected void onPostResume() { super.onPostResume(); } @Override public CharSequence onCreateDescription() { return super.onCreateDescription(); } @Override protected void onDestroy() { super.onDestroy(); } public void doSomething(){ } public void doOtherSomething(){ } }
2.5 限制局部变量的作用范围
局部变量的作用范围应该是限制为最小的(Effective Java第29条)。使用局部变量,可以增加代码的可读性和可维护性,并且降低发生错误的可能性。每个变量都应该在 最小范围的代码块中进行声明 ,该代码块的大小只要能够包含所有对该变量的使用即可。应该在第一次用到局部变量的地方对其进行声明。几乎所有局部变量声明都应该进行初始化。 如果还缺少足够的信息来正确地初始化变量,那就应该推迟声明,直至可以初始化为止 。
2.6 大括号
大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。
Bad
... ...
if(isOk)
doSomething();
else
doOtherSomething();
... ...
Good
... ...
if(isOk){
doSomething();
}else{
doOtherSomething();
}
... ...
2.7 慎用Log
记录日志会对性能产生显著的负面影响。如果日志内容不够简炼的话,很快会丧失可用性。但鉴于我们项目在 发布版本前都会删除Log ,需要必须遵守的是, 绝对不要使用System.out.println() (或本地代码中的printf()) 。System.out 和 System.err会重定向到/dev/null,因此print语句不会产生任何可见的效果。可是,这些调用中的所有字符串创建工作都仍然会执行。
3. Git约定
3.1 分支命名
- 对于每个版本的基础分支以 “develop/<当前版本号>”命名;
- 对于某个版本中的每个功能点的分支以“feature/<当前版本号>/<功能点名称>”命名;
- 对于体验优化线每个功能点的分支以“feature/ueo/<功能点名称>”命名;
- 对于某个专版渠道的版本以“custom/<当前版本号>-<专版名称>”命名;
- 原则上功能点名称不能包含develop、master、feature、ueo、custom相关字段。
3.2 版本发布注意事项
- 确认最终封版后,必须以当前的develop分支打好对应的tag,tag 名称为v_<当前版本号>;
- 输出了发布的Apk包后,必须以最终发布代码打好对应的tag,tag 名称为v_release-<当前版本号>,注意此时分支应该是已删log的代码且记得把对应的混淆输出文件(如:mapping.txt文件等)上传。
4. 结语
我们的最终目标是:保持代码的一致性,提高代码的可读性来保证代码的高质量及高维护性。
这个仅仅是初稿,有任何建议大家均可加入和修改。