编码规范

来源:互联网 发布:礼仪培训网络课程 编辑:程序博客网 时间:2024/06/05 08:58

第一章概述

前言

代码相当于程序的基石,一行行代码都是程序员的心血经过日日夜夜凝结而成的。它不仅仅是一行一行的字母+文字,它是程序员思想和经验的总结;编码规范只是大家在程序范围内达成一致的约定,这样大家的代码就可以互相看懂,维护起来更加容易,思想更畅快的交流,经验更快的得到传播。代码规范不是限制程序员的个性,应该知道,不遵守规范的个性的代码并不代表程序员的性格,并不能张扬个性。个性应该体现在用更简单、更优雅、更易读、更易理解以及算法实现效率更高等方面。

可读性,可理解性是代码的重要方面,本规范主要围绕如何去产生规范易读的代码。另外,它也保证了大家对同一代码段使用共同的语言进行描述。

术语

匈牙利命名法(Hungariannotation 通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。这些符号可以多个同时使用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。

例如:m_lpszStr,表示指向一个以0字符结尾的字符串的长指针成员变量。
骆驼命名法(Camel 混合使用大小写字母来构成变量和函数的名字
例如:person, orderDetail, oilTank
帕斯卡命名法(Pascal 与骆驼命名法类似,所有单词第一个字母大写,其它字母小写
例如:OilLevel, CustomerName

约束

没有一个规范可以到处适用,也不可能无所不包。

  1. 本规范并非完全强制性规范,对于任何违背本规范但能提高代码可读性的措施,都可以采用。
  2. 本规范第 3,4,5 章为强制性规范,其它章节为建议规范。如违背本条,请参考第一条。

第二章一般规则

  1. 所有包,类,接口,方法,属性,变量,参数均使用英文单词进行命名,具体细节请参见命名规范一章。
  2. 命名包,类,接口,方法以及变量时,尽量使用贴近问题域的表意丰富的名称。
  3. 修改源代码时,应尽量保持与所修改系统的编码风格保持一致。修改时,请注释掉修改的内容,并注明修改的时间及原因。
  4. 所有包名使用必须使用com.alibaba前缀,所有项目使用com.alibaba.aliai.[project name], project name 是项目的开发代号或缩写。

第三章格式规范

  1. 包的导入应该按照相关性进行分组。

importjava.io.IOException;

importjava.net.URL;

importjava.rmi.RmiServer;

importjava.rmi.server.Server;

importjavax.swing.JPanel;

importjavax.swing.event.ActionEvent;

importorg.linux.apache.server.SoapServer;

  1. 只导入明确需要的类,这样只要看导入列表,就可以知道该类依赖于哪些类和接口,保证可读性。

importjava.util.List; // 避免: importjava.util.*

importjava.util.Arraylist;

importjava.util.HashSet;

  1. 类和接口中元素的布局顺序。
    1. 类和接口的文档描述
    2. 类和接口的声明
    3. 类的静态变量,按照 public,protected,package,private 的顺序。
    4. 实例变量,按照 public,protected,package,private 的顺序。
    5. 类的方法,无固定顺序。
  2. 方法修饰关键字定义顺序_

<public, protected, private> staticabstractsynchronized

unuaual finalnativemethodName

注意访问标示符一定要在最前面。

publicstaticdoublesquare(doublea);

//避免: static public double square(double a);

  1. 变量声明,请使用骆驼命名法进行命名,不要在一行声明多个变量。

//推荐

intlevel;

intsize;

//避免

intlevel, size;

  1. 数组指示符紧跟类型变量
    int[] a = new int[20]; // 避免: int a[] = new int[20]
    一个变量要代表独立的意思,不要在其生命周期赋予它不同的概念。

inttempValue;

tempValue = maxValue;

...

...

tempValue = minValue;

...

tempValue = anotherValue;

tempValue 在生命周期内表示了各种各样的意图,增加理解代码的难度。

应该为每个独立概念定义单独的变量:

inttempMaxValue;

inttempMinValue;

inttempAnotherValue;

  1. 仅仅循环控制变量才能出现在 for()循环中

sum = 0;

for(i = 0; i< 100; i++) \{

sum += value[i];

\}

//避免:

for(i = 0, sum = 0; i< 100; i++)\{

sum += value[i];

\}

  1. 循环变量应靠近循环体初始化

isDone = false

while(!isDone){

...

}

//避免

isDone = false;

...

...

while(!isDone){

...

}

  1. 避免长的布尔表达式,应换成多个更容易理解的表达式。

boolisFinished = (elementNo< 0) || (elementNo>maxElement);

boolisRepeatedEntry = elementNo == lastElement;

if(isFinished || isRepeatedEntry) {

 ...

}

// 避免

if((elementNo< 0) || (elementNo>maxElement)|| elementNo ==

lastElement) {

...

}

  1. 不要在条件语句中执行方法,以提高可读性

InputStream stream = File.open(fileName, "w");

if(stream != null) {

...

}

//避免

if(File.open(fileName, "w") != null)) {

...

}

  1. 代码缩进,应该使用 4 个空格为一个单位进行缩进。

publicString invoke() throwsException {

....String profileKey = "invoke: ";

....try{

........UtilTimerStack.push(profileKey);

........if(executed) {

............test = true;

.......}

....catch{

....}

}

  1. 条件语句的主要形式,即使单条语句,也要使用括号括起来。

if(condition) \{

....statements;

\}

if(condition) \{

....statements;

\} else\{

....statements;

\}

if(condition) \{

....statements;

\} elseif(condition) \{

....statements;

\} else\{

....statements;

\}

  1. switch 语句的使用格式

switch(condition) {

caseABC :

....statements;

....//穿透,一定要做出注释

caseDEF :

....statements;

....break;

caseXYZ :

....statements;

....break;

....default:

....statements;

....break;

}

  1. 空格的使用
    1. 运算符两边应该各有一个空格。
    2. Java 保留字后面应跟随一个空格。
    3. 逗号后面应跟随一个空格。
    4. 冒号两个应各有一个空格。
    5. 分号后面应跟随一个空格。
  2. 空行的使用
    1. 文件头部注释、package 语句和import 语句之间。
    2. class 之间
    3. 方法之间
    4. 方法中,变量的申明和具体代码之间。
    5. 逻辑上相关的语句段之间
    6. 块注释和行注释的前面

▽ --- 代表空行

/**

*

*/

packageXXX.XXX;

importXXX.XXX.XXX.XXX;

/**

*注释

*/

publicclassUserFileAccess {

//

privateintmyObjId;

/**

*

*/

publicUserFileAccess()

{ ・・・ \}

/**

*

*/

publicvoidgetCtlInfo() {

intcount;

String msg;

count = 100;

・・・

//实现代码注释前空行

msg = "MESSAGE";

count = dataCount;

if(count == 0) { ・・・ }

}

}

/**

*

*/

privateclassUserFileRead {...}

  1. 逻辑上紧密相关的代码块应该用一个空行分开。

// Create a new identity matrix

Matrix4x4 matrix = newMatrix4x4();

 

// Precompute angles for efficiency

doublecosAngle = Math.cos(angle);

doublesinAngle = Math.sin(angle);

 

// Specify matrix as a rotation transformation

matrix.setElement(1, 1, cosAngle);

matrix.setElement(1, 2, sinAngle);

matrix.setElement(2, 1, -sinAngle);

matrix.setElement(2, 2, cosAngle);

 

// Apply rotation

transformation.multiply(matrix);

  1. 当对 if 语句中的条件进行折行时,应该使折行的条件语句相对主功能语句再行缩进4 个空格,以突出主要功能语句。

//使用这种缩进,突出主要功能语句。

if((condition1 && condition2)

........|| (condition3 && condition4)

........||!(condition5 && condition6))

{

....doSomethingAboutIt();}

//避免使用这种缩进,主功能语句不突出。

if((condition1 && condition2)

|| (condition3 && condition4)

||!(condition5 && condition6)) { ....doSomethingAboutIt();}

 

}

  1. 三元条件运算符

可以使用如下三种表达方式,条件要用括号括起来。

alpha = (aLongBooleanExpression) ? beta : gamma;

alpha = (aLongBooleanExpression) ? beta

: gamma;

alpha = (aLongBooleanExpression)

...............? beta

...............: gamma

第四章命名规范

一般命名规范

  1. 包名应该用小写字母,不要出现下划线等符号,名词用有意义的缩写或者英文单词。

示例:

//推荐

com.esse.business

java.lang.util

//避免

com.Esse-tech.buSiness

  1. 接口命名使用帕斯卡命名法形式的表示方式,使用名词组合。可以使用字母"I"加上帕斯卡命名法形式的表示方式

Query, DataAccess,ReportBuilder
IQuery, IDataAccess,IReportBuilder

  1. 所有类命名使用帕斯卡命名法表示方式,使用名词组合。

UserManagerImpl,ClassLoader,HttpHeaderResult

实现类如需要与接口做区分,那么在后面加上Impl,就像上面的UserManagerImpl
如果已经接口已经命名为I+帕斯卡命名法形式,对应实现类可以不用加上Impl

  1. 使用名词组合或形容词去命名一个接口,接口声明了一个对象能提供的服务,也描述了一个对象的能力。一般以"able"和"ible"作为后缀,代表了一种能力。

public interface Runnable
....public void run(); 

public interface Accessible
....public ContextgetContext(); 
}

  1. 变量名和参数名使用骆驼命名法进行表示。

userName, objectFactory, entrys, list

  1. 对于常量名,使用大写字母,并使用下划线做间隔。

MAX_TIMES, DEFAULT_NAME

  1. 程序中应该使用常量代替"25","100"等"魔法数"(过了很长一段时间后,你还记得它是做什么的吗?),如:

//推荐

if(times == MAX_TIMES) {

...

}

//避免

if(times == 25) {

...

}

  1. 这样做还有一个好处,当因为需要修改这些数字的时候,比如修改25 为30,我们只需要修改一处。
  2. 方法名应该使用动词开头,使用骆驼命名法表示方式,一般由动词+名词组成。

getName, initialize, addParameter, deleteUser

  1. 缩写字母也应该保持首字母大写

exportHtmlSource(); // 避免: exportHTMLSource();

openDvdPlayer(); // 避免: openDVDPlayer();

  1. 变量的名字应该和类型名称一致

void setTopic(Topic topic) // 避免: void setTopic(Topic value)

// 避免: void setTopic(Topic aTopic)

// 避免: void setTopic(Topic t)

voidconnect(Database database)

// 避免: void connect(Database db)

// 避免: void connect(Database oracleDB)

当同时定义多个属于同一个类的变量时,把类型作为实例的后缀,如:

Point startPoint;

Point centerPoint;

这样做是为了从实例名就可以推断它的类型名称。

  1. 根据变量的作用范围,作用范围大的应该使用长名称,作用范围大,表明变量的生命周期比较长,为了有助于理解,应尽量用长名称以表达变量的真实意图。反之,对于作用范围小,可以使用一些简化的名称,比如i,j,k 等,提高编程效率。

for(inti =0;i < times; i++)\{

...

\}

特殊命名规范

  1. 使用 get/set 对类属性进行访问,这是Java 社区的核心编码规范。使用 is 前缀表示一个布尔变量和方法。

isUsed, isEmpty,isVisible,isFinished

有时也可以使用 has,can,should:

booleanhasLicense();

booleancanEvaluate();

booleanshouldAbort = false;

  1. 在查询方法中应使用 find 作为前缀

vertex.findNearestVertex();

matrix.findSmallestElement();

node.findShortestPath(Node destinationNode);

  1. 使用 initialize 做为对象初始化的方法前缀,也可以简写为init

initializeFiles();

init();

initFontSet();

  1. 对于对象集合,变量名称应使用复数。

Collection<Point> points;

int[] values;

  1. 对于抽象类,应该使用 Abstract 前缀。

AbstractReportBuilder,AbstractBeanFactory

  1. 对于表示编号的变量,应加 No 后缀。

tableNo, userNo,employeeNo

  1. 常在一起使用的对称词汇,这些词汇一起使用,方法的表达意图自然可以互相推测和演绎。

get/set

add/remove

create/destroy

start/stop

insert/delete

increment/decrement

begin/end

first/last

up/down

min/max

next/previous

old/new

open/close

show/hide

suspend/resume

  1. 避免使用否定布尔变量

boolisError; // 避免: isNoError

boolisFound; // 避免: isNotFound

  1. 异常类应该使用 Exception 做为后缀。

AccessException, RuntimeException

  1. 缺省接口实现应该使用 Default 前缀

classDefaultTableCellRenderer implementsTableCellRenderer {

...

}

  1. 对于单例类(Singleton),应该使用getInstance方法得到单例。

classUnitManager {

privatefinalstaticUnitManager instance = newUnitManager();

privateUnitManager() {

...

}

publicstaticUnitManagergetInstance() {

returninstance;

}

}

  1. 对于工厂类,进行创建对象的方法,应该使用 new 前缀

classPointFactory {

    publicPoint newPoint(...) {

        ...

    }

}

第五章注释规范

概述

代码中为什么要包含注释?

  1. 别人要调用你的程序中的公共接口,对这部分进行文档描述,使别人能够正确而有效的使用它。
  2. 除了自己,别人要阅读和维护你的代码。为了使代码更容易维护,首先要使代码更易于理解,才能在理解的基础上进行维护。对这些代码进行文档描述,将使这个过程变得更加容易。

对代码进行注释,是在代码可读性的基础上,使用自然语言对代码所表达的意思进行阐述。这并不意味着说代码可以写的很烂,注释写的很详细,这不是好的方式。好的代码是自解释的,如果代码可读性很好,命名表意丰富,清晰,一般不需要特别多的注释。对于类,主要着重要描述它的职责,即它能干什么,对于复杂的算法实现,应该使用内部实现注释,说明算法的主要思路,对于长方法,要让阅读代码的人比较容易的明白方法实现的主要流程。反之,对于一看就懂的方法,则不需要进行注释,比如get/set方法。 

一般原则

  1. 代码应该和注释保持同步,如果代码和注释不同步,则阅读代码的人会想,"到底是代码准确,还是注释准确呢?",换谁都会糊涂。
  2. 注释尽量简洁,尺度没有准确的定义,大部分人能明白即可,可以将自己的代码给同事看看。太简单的方法就不要注释了,比如上面提到的get/set 方法。

注释内容

/**

 * Project: project Name

 *

 * File Created at 2009-11-17

 * $Id$

 *

 * Copyright 2008 - 2009 Alibaba.com Croporation Limited.

 * All rights reserved.

 *

 * This software is the confidential and proprietary information of

 * Alibaba Company. ("Confidential Information").  You shall not

 * disclose such Confidential Information and shall use it only in

 * accordance with the terms of the license agreement you entered into

 * with Alibaba.com.

 */

packagecom.alibaba.aliai.utils;

 

importjava.io.Serializable;

importjava.text.Format;

importjava.util.HashMap;

importjava.util.List;

importjava.util.Map;

 

importorg.apache.commons.logging.Log;

importorg.apache.commons.logging.LogFactory;

 

importcom.alibaba.aliai.site.service.IPostJobService;

importcom.caucho.hessian.client.HessianProxyFactory;

 

//① 代码的版权信息。

//② 类描述信息,描述类的主要职责和用处。

//③ 方法描述信息,描述方法是做什么的,如何调用,最好给出调用代码示例。

//④ JavaDoc tags ,用来生成Html形式的API 文档

//⑤ 内部实现注释,用于描述复杂的算法,长方法,从为什么要这么做角度去描述

//尽可能在类描述中加入代码调用示例,使用<pre></pre>标记,提示JavaDoc 工具不要改变格式.

/**

 * DateFormat is an abstract class for date/time formatting formats and parses

 * dates or time in a language-independent manner.

 *

 * <pre>

 * myString = DateFormat.getDateInstance().format(myDate);

 * </pre>

 *

 * <pre>

 * DateFormatdf = DateFormat.getDateInstance();

 * for (inti = 0; i<myDate.length; ++i) {

 *     output.println(df.format(myDate[i]) + "; ");

 * }

 * </pre>

 *

 * @see Format

 * @see java.util.TimeZone

 * @version 1.51 10/20/10

 * @author author1,author2

 */

publicabstractclassTest extendsFormat {

    //使用@deprecated废弃方法,不要删掉它。

    /**

     * @deprecated

     */

    /*

     * Get a default date/time formatter that uses the SHORT date and the time.

     * public final static DateFormatgetInstance() { return

     * getDateTimeInstance(SHORT, SHORT); }

     */

    //包含代码调用示例

    //使用行末注释对深层嵌套代码进行注释

    publicvoidtest() {

        for(inti = 0; i< 10; i++) {

            for(intj = 0; j < 10; j++) {

                while(1== 1) {

                    if(1== 2) {

                        switch(2) {

                            case1:

                                break;

                            case2:

                                break;

                        }// end switch

                    }//end if

                }//end while

            }//end for i

        }//end for j

    }

}

;陈春霞<wb-chunxia.chen@alibaba-inc.com>;霍丁<ding.huod@alibaba-inc.com>;李如<wb-liru1985@alibaba-inc.com>;刘艳欣<wb-liuyanxin.l@alibaba-inc.com>;杨冬<wb-yangdong@alibaba-inc.com>;陈传克<wb-chenchuanke@alibaba-inc.com>;王伟<wb-xuehunbense@alibaba-inc.com>;郭子球<wb-guoziqiu@alibaba-inc.com>;莫狄<diping.hdp@alibaba-inc.com>;

第六章编码原则

  1. 对于静态方法,应该使用类名去使用,不应该用实例去引用,主要是为了体现更多的语义。

Thread.sleep(1000);

//避免,无法体现sleep 是静态方法还是实例方法

thread.sleep(1000);

  1. 对一些基本数据类型和不太可能通过继承进行扩展的类,应声明为final,提高效率。
  2. 类和方法的粒度保持适中,保持类的规模尽量短小,职责单一。小类有很多好处,易于设计,易于测试,易于理解。同样方法也要尽量的小,每个方法尽量不要超出25 行。
  3. 开闭原则,软件应该对扩展开放,对修改开放。也就是说,应该在不修改以前源代码的基础上,改变程序的行为以适应新的需求。
  4. 里氏代换原则:假设有两个类,一个是基类 Base,一个是派生类Derived,如果一个方法可以接受基类对象b 的话:method1(Base b),同样,这个方法也应该接受派生类Derived 的对象d,而不影响方法的行为。里氏代换原则是继承复用的基石。
  5. 抽象依赖原则(稳定依赖原则)。应该依赖于抽象而不依赖与具体类,抽象的类和接口是稳定的,而具体类是易变的,如果依赖于具体类,代码就会非常脆弱,失去了灵活性。
  6. 接口隔离原则,一个类对另外一个类的依赖应该建立在最小的接口之上的。
  7. 单一职责原则,如果一个类有多于一种的职责,当需求变化时,类的职责就要发生变化,而因此就会引起引用该类的代码发生改变,职责越多,这个类就容易跟更多的类产生耦合关系,而且改变一个职责,可能会影响到另外一个职责的履行。
  8. 编写代码前,先编写注释(可以认为是伪代码),先想后写。

/*\*

\* 报表构建器,主要职责:

\* 1.创建拷贝报表模版

\* 2.填充报表数据

\* 3.构建报表

\* 4.画图处理

\*/

通过编写这些伪代码,可以起到理清思路的作用,这时候再编写代码,过程就非常流畅了,不会编一会儿,想一会儿,删掉代码,再重新编。
0 0
原创粉丝点击