Top Banner
火火火 火火 uml.org.cn 计计计计计计计计计计 GEO-QT-00-733 火火火火 火火火火 火火火 火火/火火 火火 火火 火火 火火火 B0 火火 火火 火火火 B0火火火火火
53

《软件编码风格规范》wenku.uml.com.cn/document/bmgjjc/%BC%C6%CB%E3%B…  · Web view2013. 11. 6. · 计算机源代码编写规范 geo-qt-00-733 修改章节 修改内容

Feb 13, 2021

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript

《软件编码风格规范》

计算机源代码编写规范

GEO-QT-00-733

修改章节

修改内容

版本号

编写/修改

审阅

批准

所有

新发布

B0

王琳

王密

朱欣焰

B0版审批记录:

目 录

11.目的

12.适用范围

13.读者对象

14.基本编码风格指导

14.1.标识符命名及书写

14.1.1.标识符的命名

24.1.2.源代码文件标识符命名

24.2.注释及格式要求

24.2.1.源代码文件的注释

24.2.2.函数或过程的注释

34.2.3.语句的注释

34.2.4.常量和变量的注释

34.2.5.控制结构的缩进

34.2.6.缩进的限制

44.3.数据说明

44.4.语句结构

44.5.代码编写要求

44.5.1.基本要求

54.5.2.可读性要求

54.5.3.结构化要求

54.5.4.正确性与容错性要求

64.5.5.可重用性要求

65.VC编码规范

65.1.命名

65.1.1.变量名、宏

65.1.2.局部变量(或自动变量)

65.1.3.全局变量

65.1.4.类成员变量(或类数据成员)

65.1.5.函数变量

65.1.6.宏

75.2.枚举定义风格

75.3.输出动态库约定

75.4.常用数据类型约定符

75.4.1.字符、字符串

85.4.2.整型

85.4.3.浮点数

85.4.4.不定类型指针

95.4.5.布尔型、字节型

95.4.6.句柄 (32bits)

95.4.7.Windows 结构变量

105.4.8.Windows MFC库中定义的独立对象结构和集合类

115.5.GeoStar类实例变量(对象)定义

115.6.6.6.函数名

115.6.1.全局函数名

115.6.2.一般函数

125.7.类定义风格

125.8.接口定义风格

125.9.Windows中几个常用宏

125.10.IDL参数的定义

126.Dephi编码规范

126.1.源程序书写规范

126.1.1.通用源代码格式规则

126.1.1.1.缩进

136.1.1.2.边距

136.1.1.3.begin...end 语句

136.1.1.4.注释

136.1.2.Object Pascal语句格式语句书写规范与用法

136.1.2.1.括号

136.1.2.2.保留字和关键字

146.1.2.3.过程和函数

156.1.2.4.变量

156.1.2.5.类型

166.1.2.6.语句

176.1.2.7.结构化异常处理

186.2.命名规范

186.2.1.过程(Procedure)与函数(Function)

186.2.1.1.命名

186.2.1.2.形参

186.2.1.3.命名冲突

186.2.2.常量(Constants)和变量(Variable)

186.2.2.1.常量

186.2.2.2.变量

196.2.3.类型(Type)

196.2.3.1.一般类型

196.2.3.2.类型(Class)

216.2.3.3.元件类型

236.2.3.4.窗体与对话框类型

256.2.3.5.数据模块类型

256.2.4.文件

256.2.4.1.窗体文件

256.2.4.2.数据模块文件

256.2.4.3.远程数据模块文件

256.2.4.4.单元文件

276.3.源程序文档注释规范

286.3.1.注释文档的一般规范

286.3.1.1.注释位置

286.3.1.2.注释块

286.3.1.3.忽略文档注释

296.3.2.单元文件注释文档格式

296.3.3.函数(属性)的注释文档格式

306.4.Delphi代码自动格式化工具

307.Java编码规范

307.1.编码规范

307.1.1.格式

307.1.1.1.缩进

317.1.1.2.间隔

327.1.1.3.空行

327.1.1.4.类成员的摆放顺序

327.1.1.5.文件格式

337.1.1.6.行最大长度

337.1.1.7.括号

337.1.1.8.标识符

337.1.1.9.类和接口

337.1.1.10.包

337.1.1.11.其它标识符

337.1.1.12.get和set方法

337.1.1.13.注释

347.1.1.14.JavaDoc

367.1.1.15.代码的自我说明

377.1.2.编码

377.1.2.1.决不要使用的结构

387.1.2.2.初始化

387.1.2.3.作用域(scope)

387.2.编码规则

387.2.1.类型与变量命名规则

397.2.2.创建类的规则

397.2.3.单元测试

397.2.4.类方法设计

397.2.5.类设计

397.2.6.尽量私有化

407.2.7.使用内部类

407.2.8.文档注释

407.2.9.使用常数定义

407.2.10.类创建处理

407.2.11.类清除处理

417.2.12.使用数组传参

417.2.13.抽象类与接口的选择

417.2.14.源文件保存

1. 目的

良好的编程风格是提高程序可靠性非常重要的手段,也是大型项目多人合作开发的技术基础。编程风格统一与否直接影响着软件的可维护性、可读性的好坏,以及日后培训和交流的难易程度,继而对软件开发成本有着直接的关系。因此,源程序的编码风格已经成为软件项目的一个重要组成部分。本规范的目的在于通过规范定义来避免不好的编程风格,增强程序的易读性,便于自己和其它程序员理解。

2. 适用范围

本规范适用于公司所有软件的源程序编写。客户有特殊要求时,则优先遵循客户提出的要求。

3. 读者对象

本文的阅读对象为项目经理、软件设计工程师、软件测试工程师、软件维护工程师,以期在软件的编码、测试、维护、移交过程中,保持一致的风格,提供一流代码。

4. 基本编码风格指导

4.1. 标识符命名及书写

4.1.1. 标识符的命名

这里的标识符是指编程语言中语法对象的名字,它们有常量名、变量名、函数名、类和类型名、文件名等。标识符的基本语法是以字母开始,由字母、数字、下划线组成的单词。标识符构成:类型标识_含义标识

· 类型标识:用来标明该标识的归类特征,以便与其它类型的标识互相区别。例如:字符串变量标识符的前缀为s,某字符串变量可命名为:s_Example;文本框对象标识符的前缀为t,某文本框对象的命名可为:t_Example;

· 含义标识:用来标明该标识所对应的被抽象的实体,以便记忆。上面例子中“s_Example”的“Example”就是含义标识。

· 以C++为例:C++ 的成员变量开始要冠以m_,全局变量开始要冠以g_,指针开始要冠以p_。如用 m_CountTimes 表示进行计算的次数,用 m_MessageLength 表示消息的长度。

在命名时应注意:

· 标识符本身最好能够表明其自身的含义,以便于使用和他人阅读。按其在应用中的含义由一个或多个词组成。可以是英文词或中文拼音词。

· 当标识符由多个词组成时,每个词的第一个字母大写,其余全部小写,常量标识符全部大写。中文词由中文描述含义的每个汉字的头一个拼音字母组成。英文词尽量不缩写,如果有缩写,应注意缩写规则要一致,并要给每个名字加注释。同时,一个变量只能表示一个意义。

· 标识的总长度不要超过32个字符。

4.1.2. 源代码文件标识符命名

源代码文件标识符构成:内容标识.类型标识,这两部分字符应仅使用字母、数字和下划线。标识的长度不能超过32个字符,以便于识别。

· 内容标识:通常以该文件所表示的内容或作用来命名。一般以英文表示。当由多个词组成时,每个词的第一个字母大写,其余全部小写。

· 类型标识:表示该文件的类型。具体的编程环境有特殊规定的以编程环境的规定为准。

4.2. 注释及格式要求

注释总是加在程序的需要一个概括性说明或不易理解或易理解错的地方。注释应简炼、易懂、准确。所采用的语种建议是中文,如有输入困难、编译环境限制或特殊需求也可采用英文。注释与代码的比例一般要求为1:3。即平均每写三行代码,要加有一行注释。

4.2.1. 源代码文件的注释

· 在文件的头部必须标明程序名称,它所完成的主要功能;

· 主要算法;

· 接口说明(包括调用形式,参数描述,子程序清单);

· 有关数据描述(重要的变量及其用途,约束或限制条件,以及其他有关信息);

· 模块位置(在哪个源文件中,或隶属于哪个软件包);

· 开发简历(模块设计者,复审者,复审日期,修改日期及有关说明)等;

· 维护过程中需要修改程序时,应在被修改语句前面注明修改时间和原因说明。

4.2.2. 函数或过程的注释

· 在函数头部必须对函数进行功能和参数(值参、变参)说明;

· 在函数的主体部分,如算法复杂时,应以注释的方式对其算法结构作出说明;

· 函数申请过全局资源且有可能导致资源紧张应加以注明(如内存,文件柄等);

· 函数有副作用一定以十分醒目的方式(如加!号等)注明;

· 函数的长度在100语句行以内(不包括注释),程序有特殊要求时(如速度要求等)可以例外。

4.2.3. 语句的注释

· 应对不易理解的分支条件表达式加注释;

· 不易理解的循环,应说明出口条件(有GOTO的程序还应说明入口条件);

· 过长的函数实现,应将其语句按实现的功能分段加以概括性说明;

· 供别的文件或函数调用的函数,绝不应使用全局变量交换数据。

4.2.4. 常量和变量的注释

在常量名字(或有宏机制的语言中的宏)声明后应对该名字作适当注释,注释说明的要点是:

· 被保存值的含义(必须);

· 合法取值的范围(可选);

· 全局量需要对以上逐点做充分的说明。

· 缩进规则

4.2.5. 控制结构的缩进

程序应以缩进形式展现程序的块结构和控制结构,在不影响展示程序结构的前提下尽可能地减少缩进的层次。采用如下两种缩进方式之一:

方式一:

方式二:

if (expression )

{statements

}

else

{

statements

}

if (expression ){

statements

}

else{

statements

}

4.2.6. 缩进的限制

一个程序的宽度如果超出页宽或屏宽,将是很难读的。所以本规范要求使用折行缩进的方法、合并表达式或编写子程序的方法来限制程序的宽度。

· 任何一个程序最大行宽不得超过80列,超过者应折行书写。

· 建议一个函数的缩进不得超过5级,超过者应将其子块写为子函数;

· 算法或程序本身的特性有特殊要求时,可以超过5级。

4.3. 数据说明

数据说明的次序应当规范化,使数据属性容易查找,也有利于测试和维护。基本排列次序如下:

常量说明(简单变量类型说明(数组说明(公共数据块说明(所有的文件说明。

简单变量类型说明又可进一步按下面顺序排列:

整型(实型(浮点型(字符型(…;

当多个变量用一个语句说明时,要对这些变量按字母顺序排列,如:int, high, length, size, width;如果数据结构比较复杂,如链表,要使用注释来加以解释说明。

4.4. 语句结构

· 一行内只写一条语句。

· 每个功能函数的代码长度不能过长,通常不能超过150行。

· 尽量使用库函数。

· 程序编写要首先注重清晰性及正确性,然后是执行效率,不要追求技巧。

· 尽量使用公共过程或子函数去代替重复的功能代码段。

· 使用括号来清晰地表达算术表达式和逻辑表达式的运算顺序。

· 尽量减少使用“否定”条件的条件语句。

· 程序设计模块化,模块功能单一化。

· 利用信息隐蔽,确保每一个模块的独立性。

· 确保变量使用前都进行初始化。

· 避免运算错误,例如用零做除数。

4.5. 代码编写要求

4.5.1. 基本要求

· 程序结构清晰,简单易懂,单个函数的程序行数不得超过150行。

· 代码精简,避免垃圾程序。

· 尽量使用标准库函数和公共函数。

· 不要随意定义全局变量,尽量使用局部变量。

· 使用括号以避免二义性。

4.5.2. 可读性要求

· 可读性第一,效率第二。

· 保持注释与代码完全一致。

· 每个源程序文件,都有文件头说明。

· 每个函数,都有函数头说明。

· 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义。

· 常量定义(DEFINE)有相应说明。

· 处理过程的每个阶段都有相关注释说明。

· 在典型算法前都有注释。

· 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab

· 为4个字节。

· 循环、分支层次不要超过五层。

· 注释可以与语句在同一行,也可以在上行。

· 空行和空白字符也是一种特殊注释。

· 一目了然的语句不加注释。

· 注释的作用范围可以为:定义、引用、条件分支以及一段代码。

· 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3 。

4.5.3. 结构化要求

· 禁止出现两条等价的支路。

· 禁止GOTO语句。

· 用 IF 语句来强调只执行两组语句中的一组。禁止 ELSE GOTO 和 ELSE。

· RETURN。

· 用 CASE 实现多路分支。

· 避免从循环引出多个出口。

· 函数只有一个出口。

· 不使用条件赋值语句。

· 避免不必要的分支。

· 不要轻易用条件分支去替换逻辑表达式。

4.5.4. 正确性与容错性要求

· 程序首先是正确,其次是优美。

· 修改一个错误时可能产生新的错误,因此在修改前首先考虑对其它程序的影响。

· 所有变量在调用前必须被初始化。

· 对所有的用户输入,必须进行合法性检查。

· 不要比较浮点数的相等,如: 10.0 * 0.1 == 0。

· 程序与环境或状态发生关系时,必须主动去处理发生的意外事件,如文件能否逻辑锁定、打印机是否联机等。

· 单元测试也是编程的一部份,提交联调测试的程序必须通过单元测试。

4.5.5. 可重用性要求

· 可重复使用的完成相对独立功能的算法或代码应抽象为公共控件或类。

· 公共控件或类应减少外界联系,考虑独立性或封装性。

· 公共控件或类应建立使用模板。

5. VC编码规范

5.1. 命名

5.1.1. 变量名、宏

变量名采用通常的“匈牙利”表示法,外加作用域限定符。例如:m_nCountValue,其中“m”表示作用域限定符,意为该变量是某个类的数据成员,紧接着的小写n表示变量为整型(int或long或INT或LONG),后面以大写开头的单词词组表示变量的含意。

5.1.2. 局部变量(或自动变量)

局部变量不需要加作用域限定符,如pDoc, hDC, hInstance, hPen等等。

5.1.3. 全局变量

全局变量以“g_”开头,作为作用域限定符。例如,g_bModified, g_ptStartPoint, g_strNoteText,等等。

5.1.4. 类成员变量(或类数据成员)

类成员变量统一遵守MicroSoft Foundation Classes(MFC)的约定,即所有类的数据成员(或类成员变量)都以m_作为作用域限定符开头。例如,m_bStripTrailingSpaces, m_strConnect, m_listRecordsets等等。

5.1.5. 函数变量

函数变量的定义同局部变量,不加作用域限定符。

5.1.6. 宏

宏全部以大写字母表示。

5.2. 枚举定义风格

枚举定义风格遵守Visual C++的APP Studio工作台为枚举定义的风格。

用“GEO”标识是Geostar自定义的枚举。定义风格如下:

typedef enum geoFIELDTYPE

{

GEO_FieldType_SmallInteger = 0 ,

GEO_FieldType_Integer = 1,

GEO_FieldType_Single = 2,

GEO_FieldType_Double = 3,

GEO_FieldType_String = 4,

GEO_FieldType_Date = 5,

GEO_FieldType_Geometry = 6,

GEO_FieldType_Blob = 7

} geoFIELDTYPE ;

枚举类型的定义前加小写geo,后面不加“_”,如geoFIELDTYPE。枚举元素的定义前加“GEO_”,枚举类型(如FIELDTYPE,如类型不长,则用类型全称,如类型太长则用类型简写,如FT),再加元素的定义构成整个元素的定义,如GEO_FieldType_SmallInteger。

5.3. 输出动态库约定

· 输出动态库dll、ocx、tlb和执行程序exe前加“Geo”;

· Debug版的输出动态库dll、ocx,文件名后加“_d”表示Debug版;

5.4. 常用数据类型约定符

由于在Windows NT或Windows 95的WIN32环境下,采用线性内存管理,所有的指针类型都是32bits上,再没有长、短指针之分。所以,凡是涉及到指针类型,都在前面加’p’(字符串除外)。

5.4.1. 字符、字符串

char (8)

chKeyIn;

unsigned char(8)

uchValue;

UCHAR=unsigned char;

uchValue;

Char *

szText;

PSZ=char *;

szValue;

LPSTR

szSeparator;

LPCSTR

szSQL;

PUCHAR=UCHAR *;

uszValue;

TCHAR

LPTSTR

LPCTSTR

5.4.2. 整型

(32bits)

INT = int;

nValue;

int

nValue;

long

nValue;

PINT = int *;

pnValue;

LPINT = int *;

pnValue;

LPLONG = long *;

pnValue;

UINT = unsigned int;

dwValue;

ULONG = unsigned long;

dwValue;

DWORD = unsigned long;

dwValue;

PUINT = unsigned int *;

pdwValue;

PULONG = ULONG *;

pdwValue;

PDWORD = DWORD *;

pdwValue;

LPDWORD = DWORD *;

pdwValue;

(16bits)

Short

snValue;

SHORT = short;

snValue;

USHORT = unsigned short;

wValue;

PUSHORT = USHORT *;(32)

pwValue

WORD = unsigned short; (16bits)

wValue;

PWORD = WORD *;(32)

pwValue;

LPWORD = WORD *;(32)

pwValue;

(32bits)

WPARAM = UINT;

dwParam;

LPARAM = LONG;

lParam;

LRESULT = LONG;

lResult;

COLORREF = DWORD;

clrValue;

PCOLORREF = DWORD *;

pclrValue;

5.4.3. 浮点数

float (32)

fValue;

FLOAT = float ; (32)

fValue;

float *(32)

pfValue;

PFLOAT = FLOAT *; (32)

pfValue;

double(64)

dfValue;

double *(32)

pdfValue;

5.4.4. 不定类型指针

void *

pvValue;

LPVOID = void *;

pvValue;

LPCVOID = CONST void *;

pvValue;

5.4.5. 布尔型、字节型

BOOL = int; (32)

bValue;

PBOOL = BOOL *;(32)

pbValue;

LPBOOL = BOOL *;(32)

pbValue;

BYTE = unsigned char; (8)

byteValue;

PBYTE = BYTE *;(32)

pbyteValue;

LPBYTE = BYTE *; (32)

pbyteValue;

5.4.6. 句柄 (32bits)

大部分句柄采用小写字母‘h’加上能够表示具体句柄类型的单词组成,而作用域限定符采用与上面各种变量相同的规律,例如:hInstance,hBrush,hOldBrush等等。

在编程中,注意尽量采用具体的句柄类型,不要使用通用类型代替。编译时使用STRICT选项和三级或四级警告。

5.4.7. Windows 结构变量

RECT rcClientRect; (128)

LPRECT prcDrawRect; (32)

LPCRECT prcRectValue; (32)

typedef struct tagRECT

{

LONG left;

LONG top;

LONG right;

LONG bottom;

} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

typedef const RECT FAR * LPCRECT;

RECTL rclWindowRect;

typedef struct_RECTL /* rcl */

{

LONG left;

LONG top;

LONG right;

LONG bottom;

} RECTL, *PRECTL, *LPRECTL;

typedef const RECTL FAR* LPCRECTL;

POINT ptBeginPoint; (64)

LPPOINT pptPointArray;

typedef struct tagPOINT

{

LONG x;

LONG y;

}POINT, *PPOINT, NEAR *NPPOINT; FAR *LPPOINT;

POINTL ptlBeginPoint;

typedef struct_POINTL /* ptl */

{

LONG x;

LONG y;

}POINTL, *PPOINTL;

SIZE sizeLast;

LPSIZE psizeLast;

typedef struct tagSIZE

{

LONG cx;

LONG cy;

}SIZE, *PSIZE, *LPSIZE;

typedef SIZE SIZEL;

typedef SIZE *PSIZEL, *LPSIZEL;

POINTS ptsPoint;

LPPOINTS pptsPoint;

typedef struct tagPOINTS

{

SHORT x;

SHORT y;

}POINTS, *PPOINTS, *LPPOINTS;

5.4.8. Windows MFC库中定义的独立对象结构和集合类

这一类变量的作用域限定符约定同上所述。

CString strStream;

CString * pstrStr; (32bits 指针)

CTime tmCurrent;

CTime * ptmCurrent;

CRect rcRect;

CRect * prcRect;

CPoint ptPoint;

CPoint * pptPoint;

CSize sizeDistance;

Csize * psizeDistance;

对于集合类,如:CByteArray, CDWordArray, CObArray, CPtrArray, CStringArray等等,定义时应尽量描述出其类型,区分开数组集合类(Array),链表类(List),映射类(Map)。如:m_listRecordsets, dwArrayOid等。

5.5. GeoStar类实例变量(对象)定义

GeoStar基本类定义用‘CGeo’开头,对象定义以‘g’开头,后跟能够简洁表示类型的小写串,和大写开头的变量名单词词组。

CGeoObject

gobjBasicObject;

CGeoIndex

gidxMyIndexObject;

CGeoPoint

gptNodePoint;

CGeoArc

garcFirstArc;

CGeoLine

glinTempLine;

CGeoSurface

gsurLeftSurface;

CGeoComplex

gcomTempComplexObject;

CGeoAnnotation

gannTextObject;

CGeoDBIndex

gdbidxMyIndexObject;

CGeoDBObject

gdbobjMyObject;

CGeoDBPoint

gdbptMyPointObject;

CGeoDBLine

gdblinMyLineObject;

CGeoDBSurface

gdbsurMySurfaceObject;

CGeoDBComplex

gdbcomMyComplexObject;

CGeoDBAnnotationg

gdbannMyAnnoObject;

CGeoDatabase

gdbMyDBObject;

6.6.函数名

5.5.1. 全局函数名

全局函数名在编程中应该予以统一,尤其是对于面向对象的编程。GeoStar中统一规定全局函数名一律以‘G_’开头,例如:G_GetRectIndex(DWORD *, LPCRECT);

5.5.2. 一般函数

一般函数或类的成员函数名称不作限定,但不要与全局函数名混淆,即不得以‘G_’开头。函数名应尽量表达出该函数的用途和意思。

5.6. 类定义风格

类定义风格遵守Visual C++的APP Studio工作台为MFC框架结构定义的类风格,对不同类型进行分类。用“Geo”标识是Geostar自定义的类。定义风格如下:

class CGeoDimInfo : IUnknown

{}

5.7. 接口定义风格

接口定义风格遵守COM接口定义的风格。定义风格如下:

interface IFields : IUnknown

5.8. Windows中几个常用宏

#define MAX_PATH

260

#define max (a, b)

(((a) > (b))?(a) : (b))

#define min (a, b)

(((a) < (b))?(a) : (b))

#define MAKEWORD(a, b)

((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b)))<<8))

#define MAKELONG(a, b)

((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b)))<<16))

#define LOWORD(1)

((WORD)(1))

#define HIWORD(1)

((WORD)(((DWORD)(1)>>16) & 0xFFFF))

#define LOBYTE(w)

((BYTE)(w))

#define HIBYTE(w)

((BYTE)(((WORD)(w)>>8) & 0xFF))

IDL参数的定义

单个字母的函数和参数用大写,包括结构中的变量;如X,Y,M。每一个单词的第一个字母大写,后面的小写,如Value,不能写成value或VALUE。

如果单指针作为参数前面加小写p,如pPoint。如果双指针作为参数前面加小写pp,如ppPoint。包含普通类型和接口,如IPoint * pPoint, IPoint ** ppPoint, double * pLength, double** ppLength。把参数定义成描述相关的含义更好,如newPoints,MaxScale等等。

6. Dephi编码规范

6.1. 源程序书写规范

6.1.1. 通用源代码格式规则

6.1.1.1. 缩进

缩进就是每级间有两个空格。不要在源代码中放置制表符。这是因为制表符的宽度随着不同的设置和代码管理实用程序(打印、文档及版本控制等)而不同。

通过使用Tools|Environment 菜单,在Environment Options 对话框的General页上,不要选中Use Tab Character 和Optional Fill 复选框,这样,制表符就不会被保存。

6.1.1.2. 边距

边距设置为80个字符。源代码一般不会因写一个单词而超过边距,但本规则比较灵活。只要可能,长度超过一行的语句应当用逗号或运算符换行。换行后,应缩进两个字符。

6.1.1.3. begin...end 语句

begin 语句必须单独占一行。

for i:=0 to 10 do

begin

本规则的一个特殊情况是,当begin 为else 语句的一部分时,例如:

if some statement = thenbegin  . . .endelse begin  Some Other Statement;end;

注意:end 语句总单独一行。当begin 不为else 语句的一部分时,相应的end 语句与begin 语句的缩进量相同。

6.1.1.4. 注释

使用“{...}”类型的块注释,以前的“(*...*)”类型的块注释用于临时注释掉暂不使用的代码,从Delphi 2开始支持“//”行注释,如果决定不支持Delphi 2.0以下的版本,可以使用“//”注释。

6.1.2. Object Pascal语句格式语句书写规范与用法

6.1.2.1. 括号

在左括号与下一字符之间没有空格。同样,右括号与前一字符也没有空格。

CallProc(Aparameter);

6.1.2.2. 保留字和关键字

Object Pascal 语言的保留字和关键字总是完全的小写。下面是Delphi 5保留字列表:

And

array

as

asm

Begin

case

class

const

constructor

destructor

dispinterface

div

Do

downto

else

end

Except

exports

file

finalization

Finally

for

function

goto

If

implementation

in

inherited

initialization

inline

interface

is

Label

library

mod

nil

Not

object

of

or

Out

packed

procedure

program

property

raise

record

repeat

resourcestring

set

shl

shr

String

then

threadvar

to

Try

type

unit

until

Uses

var

while

with

Xor

private

protected

public

published

automated

 

 

6.1.2.3. 过程和函数

6.1.2.3.1. 格式

过程名应当以大写字母开始,且大小写交错以增加可读性。

procedure ThisIsMuchMoreReadableRoutineName;

6.1.2.3.2. 形参

6.1.2.3.2.1. 格式

只要可能,同一类型的形参应当归并在一起:

procedure Foo(Param1,Param2,Param3:Imteger;Param4:string);

6.1.2.3.2.2. 参数顺序

形参的顺序主要要考虑寄存器调用规则。最常用的参数应当作为第一个参数,按使用频率依次从左到右排。输入参数位于输出参数之前。范围大的参数应当放在范围小的参数之前。例如:

SomeProc(aPlanet, aContinent, aCountry, aState, aCity).

有些则例外。例如,在事件处理过程中,TObject 类型的Sender 参数往往是第一个要传递的参数。

6.1.2.3.2.3. 常量参数

要使记录、数组、短字符串或接口类型的参数不能被过程修改,就应当把形参标以const。这样,编译器将以最有效的方式生成代码,保证传递的参数不可变。

如果其他类型的参数希望不被过程所修改,也可以标上const。尽管这对效率没有影响,但这给过程的调用者带来了更多的信息。

6.1.2.4. 变量

6.1.2.4.1. 局部变量

局部变量用于过程内部,如果需要的话,应当在过程的入口处立即初始化变量。局部的AnsiString 类型的变量自动被初始化为空字符串,局部的接口和dispinterface类型的变量自动被初始化为nil,局部的Variant和OleVariant类型的变量自动被初始化为Unassigned。

6.1.2.4.2. 全局变量

一般不鼓励使用全局变量。不过,有时候需要用到。即使如此,也应当把全局变量限制在需要的环境中。例如,一个全局变量可能只在单元的实现部分是全局的。

全局数据如果将由许多单元使用,就应移动到一个公用单元里被所有对象使用。全局数据可在声明时直接初始化为一个值。注意,所有全局变量自动进行零初始化,因此,不要将全局变量初始化为诸如0 、nil、或Unassigned等空值。零初始化的全局变量在.EXE文件中不占空间。零初始化的数据保存在虚拟的数据段中,而虚拟数据段只在应用程序启动时才分配内存。非零初始化的全局数据则在.EXE文件中占空间。

6.1.2.5. 类型

6.1.2.5.1. 大小写规则

类型标识符是保留字,应当全部小写。Win32 API类型常常全部大写,并且遵循诸如Windows.pas或其他API单元中关于特定类型名的规则。对于其他变量名,第一个字母应小写,其他字母则大小写交错。下面是一些例子:

var  myString: string; // 保留字  windowsHandle: HWND; // Win32 API 类型  i: Integer; //在System单元中引入的类型标识

6.1.2.5.2. 浮点型

不鼓励使用Real类型,因为它只是为了与老的Pascal代码兼容而保留的。通常情况下,对于浮点数应当使用Double。Double可被处理器优化,是IEEE定义的标准的数据格式。当需要比Double提供的范围更大时,可以使用Extend。Extend是intel专用的类型,Java不支持。当浮点变量的物理字节数很重要时(可能使用其他语言编写DLL),则应当使用Single。

6.1.2.5.3. Variant和OleVariant

一般不建议使用Variant和OleVariant。但是,当数据类型只有在运行期才知道时(常常是在COM和数据库应用的程序中),这两个类型对编程就有必要。当进行诸如自动化ActiveX控件的COM编程时,应当使用OleVariant;而对于非COM编程,则应当使用Variant。这是因为,Variant能够有效地保存Delphi的原生字符串,而OleVariant则将所有字符串转换为OLE字符串(即WideChar字符串),且没有引用计数功能。

6.1.2.6. 语句

6.1.2.6.1. If 语句

在if/then/else语句中,最有可能执行的情况应放在then子句中,不太可能的情况放在else子句中。为了避免出现许多if语句,可以使用case语句代替。如果多于5级,不要使用if语句。请改用更清楚的方法。不要在if语句中使用多余的括号。

如果在if语句中有多个条件要测试,应按照计算的复杂程度从右向左排。这样,可以使代码充分利用编译器的短路估算逻辑。例如,如果Condition1比Condition2快,Condition2比Condition3快,则if语句一般应这样构造:

if Condition1 and Condition2 and Condition3 then

如果Condition3为False的机会很大,利用短路估算逻辑,我们也可以将Condition3放在最前面:

if Condition3 and Condition1 and Condition2 then

6.1.2.6.2. case 语句

6.1.2.6.2.1. 概述

case语句中每种情况的常量应当按数字或字母的顺序排列。每种情况的动作语句应当简短且通常不超过4 - 5 行代码。如果动作太复杂,应将代码单独放在一个过程或函数中。Case语句的else子句只用于默认情况或错误检测。

6.1.2.6.2.2. 格式

case语句遵循一般的缩进和命名规则。

6.1.2.6.3. while 语句

建议不要使用Exit过程来退出while循环。如果需要的话,应当使用循环条件退出循环。所有对while循环进行初始化的代码应当位于while入口前,且不要被无关的语句隔开。任何业务的辅助工作都应在循环后立即进行。

6.1.2.6.4. for 语句

如果循环次数是确定的,应当用for语句代替while语句。

6.1.2.6.5. repeat 语句

repeat语句类似于while循环,且遵循同样的规则。

6.1.2.6.6. with 语句

6.1.2.6.6.1. 概述

with语句应小心使用。要避免过度使用with语句,尤其是在with语句中使用多个对象或记录。例如:

with Record1,Record2 do

这些情况很容易迷惑编程人员,且导致调试困难。

6.1.2.6.6.2. 格式

with语句也遵循本章关于命名和缩进的规则。

6.1.2.7. 结构化异常处理

6.1.2.7.1. 概述

异常处理主要用于纠正错误和保护资源。这意味着,凡是分配资源的地方,都必须使用try...finally来保证资源得到释放。不过,如果是在单元的初始/结束部分或者对象的构造器/析构器中来分配/释放资源则例外。

6.1.2.7.2. try...finally的用法

someClass1 := nil;someClass2 := nil;try  someClass1 := TSomeClass.Create;  someClass2 := TSomeClass.Create;  { do some code }finally  FreeAndNil(someClass1);  FreeAndNil(someClass2);end;

6.1.2.7.3. try...except的用法

如果你希望在发生异常时执行一些任务,可以使用try...except通常,没有必要为了简单地显示一个错误信息而使用try...except,因为Application对象能够自动根据上下文做到这一点。

6.1.2.7.4. try...except...else的用法

不鼓励使用带else子句的try...except,因为这将阻塞所有的异常,包括你没有准备处理的异常。

6.2. 命名规范

6.2.1. 过程(Procedure)与函数(Function)

6.2.1.1. 命名

过程与函数名应当有意义。进行一个动作的过程最好在名称前加上表示动作的动词为前缀。例如:

procedure FormatHardDrive;

设置输入参数值的过程名应当以Set 为其前缀,例如:

procedure SetUserName;

获取数值的过程名应当以Get 为其前缀,例如:

function GetUserName:string;

6.2.1.2. 形参

所有形参的名称都应当表达出它的用途。如果合适的话,形参的名称最好以字母A(Attribute)为前缀,例如:

procedure SomeProc(AUserName:string; AUserAge:integer);

当参数名与类的特性或字段同名时,前缀A就有必要了。

6.2.1.3. 命名冲突

当两个单元中含有相同名称的过程时,如果调用该过程,实际被调用的是Uses 子句中较后出现的那个单元中的过程。为避免这种情况,可在方法名前加想要的单元名,例如:

SysUtils.FindClose(sr); 或Windows.FindClose(handle);

6.2.2. 常量(Constants)和变量(Variable)

6.2.2.1. 常量

常量的名称应当能够表达出它的用途。字符串资源常量(ResourceString)一般是以rs为前缀。

6.2.2.2. 变量

变量的名称应当能够表达出它的用途。循环控制变量常常为单个字母,诸如i、j或k。也可以使用更有意义的名称,例如userIndex。布尔变量名必须能清楚表示出True 和False 值的意义。

6.2.2.2.1. 局部变量

局部变量遵循其他变量的命名规则。

6.2.2.2.2. 全局变量

全局变量一般以大写字母“g”打头,并遵循其他变量的命名规则。

6.2.3. 类型(Type)

6.2.3.1. 一般类型

6.2.3.1.1. 枚举型

枚举类型名必须代表枚举的用途。名称前要加T字符作为前缀,表示这是个数据类型。枚举类型的标识符列表的前缀应包含2 - 3 个小写字符,来彼此关联。

枚举类型的变量实例的名称与类型相同,但没有前缀T,也可以给变量一个更加特殊名称,诸如:FavoriteSongTypel、FavoriteSongType2等等。

6.2.3.1.2. 构造类型

6.2.3.1.3. 数组类型

数组类型名应表达出该数组的用途。类型名必须加字母“T”为前缀。如果要声明一个指向数组类型的指针,则必须加字母P 为前缀,且声明在类型声明之前。例如:

type  PCycleArray = ^TCycleArray;  TCycleArray=array[1..100] of integer;

实际上,数组类型的变量实例与类型名称相同,但没有“T”前缀。

6.2.3.1.4. 记录类型

记录类型名应表达出记录的用途。类型名必须加字母T为前缀。如果要声明一个指向记录类型的指计,则必须加字母P为前缀,且其声明在类型声明之前。例如:

type  PEmployee = ^TEmployee;  TEmployee = record    EmployeeName: string;    EmployeeRate: Double;  end;

6.2.3.2. 类型(Class)

6.2.3.2.1. 命名与格式

类的名称应当表达出类的用途。一般的类名前要加字母“T”,如果是接口类那么类名前要加“I”,错误异常类的类名前要加“E”,而类引用类型(Class-reference type)则要在类名后加“Class”,抽象类一般是在类名前还要加“Custom”。例如:

type  TCustomCipher = class(TObject);  TCipher = class(TCustomCipher);  ICipher = interface;  TCipherClass = class of TCustomer  ECipherException = class(Exception);

类的实例名称通常与类名相同,只不过没有前缀“T”。

var  customer: TCustomer;

注意:关于元件的命名,请参阅3.3.4小节。

6.2.3.2.2. 字段

6.2.3.2.2.1. 命名与格式

字段的命名遵循与变量相同的规则,只不过要加前缀f(Field),表示这是字段。

6.2.3.2.2.2. 可见性

所有字段必须为私有。如果要在类的作用域之外访问字段,可借助于类的属性来实现。

6.2.3.2.3. 方法

6.2.3.2.3.1. 命名与格式

方法的命名遵循与过程和函数相同的规则。

6.2.3.2.3.2. 静态方法

当你不希望一个方法被派生类覆盖时,应当使用静态方法。

6.2.3.2.3.3. 虚拟方法(virtual)与动态方法(dynamic)

当你希望一个方法能被派生类覆盖,应当使用虚拟方法(virtual)。如果类的方法要被多个派生类直接或间接地使用,则应当用动态方法(dynamic)。例如,某一个类含有一个被频繁覆盖的方法,并有100个派生类,则应将方法定义为动态的,这样可以减少内存的开销。

6.2.3.2.3.4. 抽象方法(abstract)

如果一个类要创建实例,则不要使用抽象方法。抽象方法只能在那些从不创建实例的基类中使用。

6.2.3.2.3.5. 属性访问方法

所有属性访问方法应当定义在类的私有或保护部分。属性访问方法遵循与过程和函数相同的规则。用于读的方法应当加“Get”前缀,用于写的方法应当加“Set”前缀,并且有一个叫Value的参数,其类型与属性的类型相同。例如:

TSomeClass = class(TObject)private  fSomeField: Integer;protected  function GetSomeField: Integer;  procedure SetSomeField(Value: Integer);public  property SomeField: Integer read GetSomeField write SetSomeField;end;

尽管不是必须,但还是建议你使用写访问方法来访问代表私有字段属性。

6.2.3.2.4. 属性

属性作为私有字段的访问器,遵循与字段相同的命名规则,只不过没有f前缀。属性名应为名词,而不是动词。属性是数据,而方法是动作。数组属性名应当是复数,而一般的属性应当是单数。

6.2.3.3. 元件类型

6.2.3.3.1. 元件类型的命名标准

元件的命名与类的命名类似,只不过当它与其它元件名称冲突时,你可以加上3个字符的前缀,用以标识公司、个人或其他实体。例如,一个时钟元件可以这样声明:

TddgClock = class(TComponent)

注意,作为前缀的3 个字符要小写。

6.2.3.3.2. 元件实例的命名规则

元件实例的名称应当能够描述其实际意义,这里命名规则使用了一个变更的匈牙利前缀命名规范。使用前缀而不使用后缀的原因是在搜寻时,在对象检查器和代码探索器中搜寻构件的名字比搜寻构件的类型更容易。在这个标准中,元件实例名包括两个部分:前缀和性质标识名。

6.2.3.3.2.1. 元件的前缀

元件的前缀多是表现元件类型的字母缩写。参见下面表中的元件前缀:

元件类名

元件前缀

TActionList, TAction表示动作的列表项

act

TButton, TSpeedButton, TBitBtn等所有的按钮类

btn

TCheckBox, TDBCheckBox等所有的检查框

chk

TRadioButton单选按钮类

rdo

TToolBar工具条

tb

TMainMenu所有的主菜单类与菜单项类

mnu

TPopupMenu所有的弹出式菜单类与弹出式菜单项类

mnu

TLabel, TStaticText等所有用来显示的标签类

lb

TPanel等所有的面板类

pnl

TPageControl等所有的页式控件类

pgc

TEdit, TMaskEdit等所有的单行编辑框类

ed

TMemo, TRichEdit等所有的多行编辑框类

mem

TDrawGrid, TStringGrid等所有的网格类

grd

TAnimate等所有的动画类

ani

TImageList等所有的图片列表类

il

TImage等图片类

img

TChart图表类

cht

TComboBox, TDBComboBox等所有的下拉式列表框类

cbo

TListBox, TDBList等所有的列表框类

lst

TTreeView

tv

TListView

lv

THotKey

hk

TSplitter等所有的分隔符类

spt

TOpenDialog等所有的对话框元件类

dlg

TTable等所有的数据表类

tbl

TQuery等所有的SQL查询类元件

qry

TClientDataSet所有的客户数据集元件

cds

TDataSource

ds

TDatabase

db

TSockConnection,TDCOMConnection等连接元件类

con

TQuickRep, TFastReport等所有的报表元件类

rpt

TDDEClientConv,TDDEClientItem等所有的DDE元件类

dde

TMonthCalendar等所有的日历类

cal

TGroupBox等控件类

grp

TForm等窗体类

frm

如上所示,元件类型前缀是从分析描述元件的类型性质而来的。通常情况下,下面的规则描述如何定义一个元件类型前缀:

· 从元件类型名中移去T前缀。例如TButton变成Button。

· 除了第一个元音,删去所有元音字母。例如,Button变成bttn,Edit变成ed。

· 压缩双字母。例如,bttn变成btn。

· 如发生冲突,则在某一元件前缀中加入一个元音。例如在TBatton元件的前缀中加入元音变为batn,以区别TButton的前缀。

不过,上述规则首先得保证前缀名称必须符合习惯,做到见名知意,如:TDDEClientConv控件的前缀就是一个例外。

注意:元件的前缀是为了表示出元件的类型,是按钮,还是标签等等,因此没有必要为每一个特别元件类建立一个元件前缀,如: TMyButton的元件前缀仍为btn。

6.2.3.3.2.2. 元件性质标识名

元件性质标识名是元件意图的描述。例如,一个用于关闭窗体的TButton元件实例可命名为btnClose。一个编辑姓名的元件实例可命名为edName。

6.2.3.4. 窗体与对话框类型

6.2.3.4.1. 窗体类型的命名标准

窗体或对话框类型的名称应当表达出窗体的用途,如果是窗体要加“Tfrm”前缀,如果是对话框要加“Tdlg”,后跟描述性名。例如,About窗体类型名称为:

TfrmAbout = class(TForm)

主窗体的类型名称为:

TfrmMain = class(TForm)

客户登录窗体的类型名称为:

TfrmCustomerEntry = class(TForm)

登录对话框的类型名称为:

TdlgLogin = class(TForm)

6.2.3.4.2. 窗体实例的命名标准

窗体实例的名称与相应的类型名称相同,但没有前缀T 。使用前缀法命名窗体和对话框的好处在于可以在Object Inspector(属性观察器)中快速找到需要的元件类型。例如,前面提到的窗体类型与实例的名称为:

类型名

实例名

TfrmAbout

frmAbout

TfrmMain

frmMain

TfrmCustomerEntry

frmCustomerEntry

TdlgLogin

dlgLogin

6.2.3.4.3. 自动创建的窗体

除非特别原因,只有主窗体才自动生成。其他所有窗体必须从Project Options对话框的自动生成列表中删除。更进一步信息,请参阅后面几节。

6.2.3.4.4. 模式窗体实例化函数

所有窗体单元都应当含有实例化函数,用于创建、设置、模式显示和释放窗体。这个函数将返回由窗体返回的模式结果。传递给这个函数的参数遵循参数传递的规则。之所以要这样封装,是为了便于代码的重用和维护。

窗体的变量应当从单元中移走,改在窗体实例化函数中作为局部变量定义(注意,要求从Project Options对话框的自动生成列表中移走该窗体。请看前面的内容。

例如,下面的单元文件演示了GetUserData的实例化函数。

Unit UserDataFrm;

Interface

Uses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls;

Type

  TfrmUserData = class(TForm)    edUserName: TEdit;    edUserID: TEdit;  private  { Private declarations }  public  { Public declarations }  end;

function GetUserData(var AUserName: String;var AUserID: Integer): Word;

implementation

{$R *.DFM}

function GetUserData(var AUserName: String;var AUserID: Integer): Word;var  frmUserData: TfrmUserData;begin  frmUserData := TfrmUserData.Create(Application);  frmUserData.Caption:='Getting User Data' ;  Result : = frmUserData.ShowModal;  if Result=mrOK then  begin    AUserName := frmUserData.edUserName.Text;    AUserID := StrToInt(frmUserData.edUserID.Text);  end;  finally    frmUserData.Free;  end;end;

End.

6.2.3.4.5. 窗体框架与复合窗体

如果一个窗体结构过于复杂,就必须将其分化成为一个主窗体框架以及嵌入到主窗体框架的若干子窗体框架。如:

TfrmMainFrame: TfrmInfoFrame,TfrmEditorFrame

使用窗体框架,主要是为了解决界面和代码复用问题,以及提高单元代码的内聚力(划分后,每一个窗体框架为一个独立单元),从而提高软件工程质量。你必须提炼出界面关联代码(可复用的)和应用关联代码(不能复用的)。

6.2.3.5. 数据模块类型

6.2.3.5.1. 数据模块的命名标准

数据模块类型名称应表达出它的用途,且要加前缀“Tdm”,后跟描述性名称。例如,Customer数据模块的类型名称为:

TdmCustomer = class(TDataModule)

Orders 数据模块的类型名称为:

TdmOrder = class(TDataModule)

6.2.3.5.2. 数据模块实例的命名标准

数据模块实例的名称应当与相应的类型名称相同,但没有前缀T。例如,前面的数据模块类型、实例名称如下:

类型名称

实例名

TdmCustomer

dmCustomer

TdmOrder

dmOrder

6.2.4. 文件

建议在所有源文件、项目文件和单元文件使用结构化的文件头信息。

6.2.4.1. 窗体文件

窗体文件的名称应当表达出窗体的用途,且具有frm前缀。例如,About窗体的文件名叫frmAbout.dfm,主窗体的文件名叫frmMain.dfm。

6.2.4.2. 数据模块文件

数据模块文件的名称应当表达出数据模块的作用,且具有dm前缀。例如,Customers数据模块的文件名叫dmCustomers.dfm。

6.2.4.3. 远程数据模块文件

远程数据模块文件的名称应当表达出远程数据模块的用途。名称后要加rdm前缀。例如,Customers远程数据模块的文件叫rdmCustomersRDM.dfm。

6.2.4.4. 单元文件

6.2.4.4.1. 普通单元

6.2.4.4.1.1. 单元名

单元的名称应当有描述性。例如,应用程序的主窗体单元叫U_MaimFrm.pas,推荐前面使用U_作为前缀。

6.2.4.4.1.2. Uses 子句

Interface部分的Uses子句应当只包含该部分需要的单元。不要包含可能由Delphi自动添加的单元名。Implementation部分的Uses子句应当只包含该部分需要的单元,不要有多余的单元。

6.2.4.4.1.3. Interface 部分

Interface部分应当只包含需要被外部单元访问的类型、变量、过程与函数的声明。而且,这些声明应当在Implementation部分之前。

6.2.4.4.1.4. Implementation 部分

Implementation部分包括本单元私有的类型、变量、过程与函数的实现。

6.2.4.4.1.5. Initialization 部分

不要在Initialization部分放置花费时间很多的代码。否则,将导致应用程序启动时显得很慢。

6.2.4.4.1.6. Finalization 部分

确保释放所有在Initialization部分中分配的资源。

6.2.4.4.2. 元件单元

6.2.4.4.2.1. 命名

元件单元应放在单独的路径中,以表明它们是定义元件的单元。它们一般与项目不放在同一路径下。单元文件名称应表达出其内容。

元件单元只能含有一个主要元件,这是指出现在元件选项板上的元件。其他辅助性的元件或对象也可以包含在同一单元中。

6.2.4.4.2.2. 注册单元

元件的注册过程应当从元件单元中移走,放在一个单独的单元中。这个注册单元用于注册所有元件、属性编辑器、元件编辑器、向导等。

元件注册应当在设计期包中进行。文件名为 xxxReg.pas。其中,xxx字符前缀,以标识元件包名称或公司、个人、其他实体。例如,注册单元命名为xxxReg.pas。

6.2.4.4.3. 包文件(.dpk)命名规则

6.2.4.4.3.1. 运行期包与设计期包

运行期包中应当只包含所需要的单元。那些属性编辑器和元件编辑器的单元应当放在设计期包中。注册单元也应当放在设计期包中。

6.2.4.4.3.2. 文件命名标准

包的命名遵循下列模式:

d cliiiDescvvCn.pkg —设计期包iiiDescvvCn.pkg    —运行期包

其中,iii代表一个2-3字符的前缀,用于标识公司、个人或其他需要标识的事情,也可不要;Desc表示该控件包的简短描述;vv代表包的版本号,你可以根据需要取舍;前缀“dcl”表示设计期包,没有该前缀表示运行期包;字母“Cn”表示编译器类型与编译器版本号,如:Delphi5=D5, Delphi4=D4, CBuilder3=C3...。

注意包名称中的lib或std分别表示这是设计期包还是运行期包。例如:

dclrbStdCompsD5.pkg —Delphi 5的设计期包rbStdCompsD5.pkg     —Delphi 5的运行期包

6.3. 源程序文档注释规范

我们书写注释的目的主要有二:一是为自己以后阅读源程序提供方便;二是为建立规范的程序文档手册,提供接口说明。我这里主要阐述的是如何建立规范的程序文档手册。

我们将通过文档(源程序文件联机注释)定义标准的Delphi 类(函数等)的概要设计规范(official Delphi Class API Specification),以及定义Delphi 类(函数等)的编程指南手册。因此,我们可以以两种方式来写程序文档(注释),一是作为概要设计的规范,二是作为编程手册。两种程序文档既有差别,又有它们共同的地方,因此,它们可以同时写在源程序文档注释中,它们两者的差别在下面有说明。

6.3.1. 注释文档的一般规范

6.3.1.1. 注释位置

为源程序的相关元素添加文档注释很简单,你只需要将注释放在元素的声明(Interface)部分(如果是接口函数或类方法)或实现(implementation)部分(如果是私有函数)。例如:

(for Object Pascal)

{ The Sort function sorts the list.}

function MyList.Sort(Options: Integer): BOOLEAN;

begin

[...]

end;

6.3.1.2. 注释块

注释块是由一系列的没有被空行分开的单行注释构成的,下面就是一个注释块:

// This is the first sentence.

// This is the second,

// and this the third

而下面的注释则是两个注释块,因为它们之间有一个空行把它们分开了:

// This is the first sentence.

// This is the second,

// and this the third

许多文档注释分析工具(如:Doc-O-Matic)会将注释块内的所有的句子自动连接在一起,形成一文字段落,如:

// This is the first sentence.

// This is the second,

// and this the third

经过分析工具生成的文档文本为:

This is the first sentence. This is the second, and this the third.

6.3.1.3. 忽略文档注释

当注释行前用“##”开头时,该行注释将被文档注释分析工具忽略,该行不会生成到文档中。如:

//## This line will be ignored.

包含有如下标识符的注释块也不会被生成到文档中去:

"Ignore Text", "$Log", "$Filename", "$Revision", "$Date", "$Author", "$History", "$Id"

6.3.2. 单元文件注释文档格式

用于描述该单元的用途,作者,功能。放于单元文件的最前面:

{

##Unit Name: %UNIT%

##Initial Date: %DATE%

Summary

该单元的摘要说明。

See Also

参阅

Bugs

已知问题。

TODO

待作事项。

Author

Riceball LEE([email protected])

Riceball LEE([email protected])

}

unit xxx;

6.3.3. 函数(属性)的注释文档格式

所有的公有函数与方法都应该加上这些注释。

{

##Procedure: %PROCNAME%

##Date: %DAY%-%MONTHSHORTNAME%-%YEAR%

Summary

该函数(属性)的摘要说明。

Parameters

%ARGUMENTS%

Returns

%RESULT%

Conditions

调用该方法(函数)可能需要的条件

Exceptions

该方法(函数)有可能触发的异常。

See Also

参阅

Bugs

已知问题。

Author

Riceball LEE([email protected])

Riceball LEE([email protected])

}

function xxx;

6.4. Delphi代码自动格式化工具

尽管大多数的代码自动格式化工具能够帮你重排源程序格式,以及更新保留字和标示符的大小写,但是这最好在使用版本控制前进行,如果你已经使用了版本控制,建议你不要轻易使用代码自动格式化工具,哪怕多一个空格,版本控制工具也会认为该行已被修改,从而给程序管理带来不便。

7. Java编码规范

7.1. 编码规范

7.1.1. 格式

7.1.1.1. 缩进

所有的缩进皆为四个空格。对应的括号通常在同一列的位置上。例如:

void foo()

{

while ( bar > 0 )

{

System.out.println();

bar-- ;

}

if ( oatmeal == tasty )

{

System.out.println("Oatmeal is good and good for you");

}

else if ( oatmeal == yak )

{

System.out.println("Oatmeal tastes like sawdust");

}

else

{

System.out.println("tell me pleeze what iz dis 'oatmeal'");

}

switch( suckFactor )

{

case 1:

System.out.println("This sucks");

break;

case 2:

System.out.println("This really sucks");

break;

case 3:

System.out.println("This seriously sucks");

break;

default:

System.out.println("whatever");

break;

}

}

· 缩进推荐使用“Space(空格)键”,而不是“Tab键”。

原因:所有的程序对于“Space”都会运行得很好。而大多数程序往往会混淆Tab和Space,以致于对有些行用Space缩进,有些行用Tabs缩进。如果你的Tab设置是4,而你又把文件共享给了一个Tab设置是8的用户,那么一切都会大乱的。

· 所有的if、while和for语句中的“状态”内容必须用括号括起来,就算只有一个状态。

原因:一致才能易读。如果代码行有的长出一块,有的短一块,那么整体编辑效果就很不好,也就失去了易读性。

如下面的比较:

if ( superHero == theTick ) System.out.println("Spoon!"); //不好!

if ( superHero == theTick ) // 好!

{

System.out.println("Spoon!");

}

7.1.1.2. 间隔

· 所有的标识符都必须被空白字符包围

原因:有些功能是在你双击某个标识符的时候被选中的。而这些通过双击选中的功能标识符,有一些是必须严格地被空格符划定界限的,所以周围的任何一个字符都有可能因为没有设置空格符而一不小心把另一个功能标识符包括在自己的标识符范围内,从而当用户双击该标识符时产生违背原先意愿的结果。由此可见,使用空白字符(空格符和换行)围绕标识符还是有必要作为一项规范的。

7.1.1.3. 空行

应该不时的在各方法之间加入一些空格行来分割大段的代码;

还应该在方法与方法之间加入一两行的空格行。

7.1.1.4. 类成员的摆放顺序

class Order

{

// final attributes

// attributes

// constructors

// methods

}

必须保持private方法被放置在使用该方法的其他方法之上,而在构造器(constructor)之下,即使该构造器有可能调用这些private方法。

7.1.1.5. 文件格式

“package”必须总保持第一个出现;

“import”其次;

再次,任何非javadoc的注释;

然后是javadoc类文件

最后便是类。

注意:一个文件(File)只能有一个类,内部类除外。

示例:

package misc ;

import java.io.* ;

import java.net.* ;

/** this class does cool stuff

@author Joe Programmer

*/

class SpaceMonkey

{

...

}

7.1.1.6. 行最大长度

不要让一行代码的长度超锅120个字符,最好是低于80个字符。如果代码开始向右延伸得很长,你就应该考虑把它分割成更多的方法。

7.1.1.7. 括号

使用括号的目的必须是在表达上不但能够标明优先顺序,而且有助于使表达更简单明了。另外,如果某一段代码有可能产生歧义,也需加括号。

7.1.1.8. 标识符

所有的标识符只能用字母(A—Z或a—z)和数字(0—9)。不能有下划线、货币符号或者其它非ASCII字符。

7.1.1.9. 类和接口

所有类和接口标识符将都使用混合“格”表示。每个名称中的每个单词首字母必须大写,同时这个名称的首字母也必须大写;其它的字母均小写,除了缩写词之外(它们必须全部大写)。

7.1.1.10. 包

所有包名只能用小写字母。尽量别使包名长度超过8个字符,应该避免使用多个词作为包名。

7.1.1.11. 其它标识符

其它标识符包括(但不限于):属性(attributes)、变量(variables)、方法(methods)和参数(parameters)。它们将沿袭一个默认的命名习惯(同样对final标识符也适用),就是:

除了名称的首字母之外,名称中其它单词的首字母大写;其余字母小写,但缩写词的每个字母大写。

7.1.1.12. get和set方法

用于设置对象状态的方法必须在方法名前面加一个前缀set;用于检索一个布尔类型对象状态的方法必须在方法名前面加一个前缀is;而用于检索其它类型对象状态的方法则必须在方法名前面加上get。

7.1.1.13. 注释

大部分注释尽量用“//”;对于所有的javadoc的注释则用“/** */”;而临时对代码块进行的注释尽量用“/* */”。

7.1.1.14. JavaDoc

· JavaDoc注释将用于说明那些被其它类调用的类、属性和方法。这些注释必须出现在所要说明的各项之前。

· JavaDoc注释一般不会用于说明一些显而易见的方法,例如:

public static void main( String[ ] args ) 或public int getX( ) ;

· JavaDoc注释也不用于说明一些显而易见的参数,如:

public void setX( int newX ) ;

· 诸如servlet和EJB等那些没有被其它类调用的类,也不必加JavaDoc注释。把源码上交给整个团队之前,必须先经过JavaDoc处理,并全面检查处理结果,以确定说明文字确实可读而且清楚明白。

如果JavaDoc注释能够在一行内写下,则格式应该象下面这样:

/** Used to mark spots */

int x ;

如果JavaDoc注释内容在一行内容纳不下,则其格式应该象下面这样:

/** Set how much to grow when growth is needed.

Smaller values will usually save memory, but frequent

reallocation may take a lot of time.

@param HowMuch The number of extra ints to allocate when

memory reallocation is required. Values must be greater than

zero.

*/

public void setExtra( int HowMuch )

{

注意:HTML标签

的作用。

迫使一段代码进行分行,而

…则让块文字以特定的字体表现出来并且保留所有的空格字符。

JavaDoc还允许使用其它的HTML标签,但是禁止使用header标签(如

, 等)你可以用..加黑文字,也可以用..使文字变为斜体。

注意:JavaDoc把每个JavaDoc注释的第一行划分出来以用于放置“内容表”。如何标识出这部分内容的结束边界线呢?JavaDoc定义这个标志为“一个句号后跟一个空格”。其它如“一个问号后跟一个空格”或“一个句号后跟一个

标签”都不是结束标志。如果在句号和

之间加一个空格,那么就有结束标志产生了。

7.1.1.14.1. 类

类的JavaDoc说明文件必须包括以下内容:

· 简要的提纲

· 详细的描述

· 使用该类的示例代码段

· 用@author标签列出作者

注意:由于JavaDoc中一个“功能(feature)”限制,所有示例代码的每行前面必须加入一个星号,以便保存每行的缩进。例如:

/** A vector class optimized for working with ints.

Like the Vector object, except rather than tracking a dynamic

array of pointers to different objects, this is simply a

dynamic array of ints. The advantage is speed and memory

savings.

Example:

*

* // report longest lines

* TextFileIn f = new TextFileIn("blather.txt");

* IntVector v = new IntVector();

* int longestLine = 0 ;

* boolean done = false ;

* while ( ! done )

* {

* String s = f.readLine();

* if ( s == null )

* {

* done = true ;

* }

* else

* {

* int sLength = s.length() ;

* if ( sLength > longestLine )

* {

* longestLine = sLength ;

* }

* v.append( sLength );

* }

* }

* f.close();

* System.out.println("The longest lines are on line numbers:");

* for ( int i = 0 ; i < v.length() ; i++ )

* {

* if ( v.get( i ) == longestLine )

* {

* System.out.println( i );

* }

* }

@author Adam Baum

@author Justin Case

*/

public class IntVector

{

7.1.1.14.2. 方法

方法的JavaDoc说明文档必须包含以下内容:

· 简要的提纲;

· 详细的描述(如果有必要在简要提纲内补充说明某些内容的话);

· 用JavaDoc的@param标签列出所有参数(如果有参数的话);

· 用JavaDoc的@return标签返回出方法的值列表(如果需要返回值的话);

· 用JavaDoc的@exception标签列出所有异常(exception)(如果有异常抛出的话)

/** Get a copy of one int.

Retrieve an int relative to the index provided.

@param Index Which int (0 is the first int).

@return The retrieved int or zero if Index is outside of 0..length.

*/

public int get( int Index )

{

7.1.1.15. 代码的自我说明

除了要尽力用文件说明程序的复杂算法,我们还必须尽量通过多用一些标识符来使程序的算法易读。这样有助于减少将来需要修改程序而不需修改说明文档而带来的麻烦。例如:

原始代码:

/** determine if the given year is a leap year.

The Gregorian calendar principal states that a leap year occurs

every fourth year, except every 100 years, except every 400

years.

If the year is evenly divisible by 400 or is evenly divisible by

4 and not by 100, then it is a leap year.

@param year The year to be tested. Make sure this is a four digit year!

@return true if "year" is a leap year.

*/

boolean isLeapYear( int year )

{

return ( ( ( y % 400 ) == 0 ) || ( ( ( y % 4 ) == 0 ) && ( ( y % 100 ) != 0 ) ) );

}

修改后的代码:

/** determine if the given year is a leap year.

The Gregorian calendar principal states that a leap year occurs

every fourth year, except every 100 years, except every 400

years.

@param year The year to be tested. Make sure this is a four digit year!

@return true if "year" is a leap year.

*/

boolean isLeapYear( int year )

{

boolean y4 = ( ( year % 4 ) == 0 ) ;

boolean y100 = ( ( year % 100 ) == 0 ) ;

boolean y400 = ( ( year % 400 ) == 0 ) ;

return ( y400 || ( y4 && ! y100 ) );

}

7.1.2. 编码

7.1.2.1. 决不要使用的结构

7.1.2.1.1. 决不要使用do…while结构

一般来说,程序员看程序总是在每个方法代码的开头查找该方法的起始处,然后顺次查找它的执行体。当遇到一个循环时,程序员的第一反应就是想知道它的循环终结点在哪里。如果你将循环结束逻辑语句放在你的代码最后,那么你的代码将极难阅读。更糟的是,有许多欠经验的程序员对do…while结构根本不熟,他们会让你重写程序的。

boolean done = false ;

while ( ! done )

{

...

}

7.1.2.1.2. 决不要在一个方法的中间使用“return”

“return”只能出现在一个方法的末尾。

这是因为在方法的中间使用“return”会给今后将方法拆分成几个更小的方法带来困难;而且它会迫使开发者不得不为该方法考虑多于一个的出口点。

7.1.2.1.3. 不要混合使用递增运算符和递减运算符

在方法调用或是数学运算中混合使用递增运算符(或递减运算符)会造成欠经验的程序员阅读的困难,他们也许会让你重写代码的。

所以,最好在递增运算符(或递减运算符)之间加上额外的行。

例如:

foo( x++ ); // 不好!

foo( x ) ; // 好!

x++ ;

y += 100 * x++ ; // 不好!

y += 100 * x ; // 好!

x++ ;

7.1.2.2. 初始化

最好总是在每个变量出现的时候就马上进行初始化。

最好只在需要的时候再声明(declare)一个变量,不然的话会影响代码的执行效果。示例:

int totalWide ;

int firstWide = 20 ;

int secondWide = 12 ;

firstWide = doFoo( firstWide , secondWide );

doBar( firstWide , secondWide );

totalWide = firstWide + secondWide ; // wrong!

int firstWide = 20 ;

int secondWide = 12 ;

firstWide = doFoo( firstWide , secondWide );

doBar( firstWide , secondWide );

int totalWide = firstWide + secondWide ; // right!

int secondWide = 12 ;

int firstWide = doFoo( 20 , secondWide );

doBar( firstWide , secondWide );

int totalWide = firstWide + secondWide ; // even better!

7.1.2.3. 作用域(scope)

除内部类之外的所有类的所有属性必须总是private。

7.2. 编码规则

7.2.1. 类型与变量命名规则

类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母。例如:

ThisIsAClassName

thisIsMethodOrFieldName

若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。

Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu等,全部都应小写(这也是Java 1和Java 2的区别之一)。

7.2.2. 创建类的规则

为了常规用途而创建一个类时,请包含对下述元素的定义:

equals() hashCode() toString() clone()(implement Cloneable) implement Serializable

7.2.3. 单元测试

对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。

7.2.4. 类方法设计

应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个 方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。

7.2.5. 类设计

使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:

· 一个复杂的开关语句:考虑采用多态机制;

· 数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现;

· 许多成员变量在特征上有很大的差别:考虑使用几个类;

7.2.6. 尽量私有化

 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。

7.2.7. 使用内部类

任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作。

7.2.8. 文档注释

尽可能细致地加上注释,并用javadoc注释文档语法生成自己的程序文档。

7.2.9. 使用常数定义

避免使用“魔术数字”(magic number),这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数(final)标识符。这样可使程序更易理解以及更易维护。

7.2.10. 类创建处理

涉及构建器和异常的时候,通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个对象已正确地创建,从而盲目地继续。

在构建器内部,只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程中产生不可预知的结果。

7.2.11. 类清除处理

当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除,并已丢弃了从RuntimeException继承的一个类(如果还没有的话),从而指出一个编程错误。在采取象这样的方案之前,请确定finalize()能够在自己的系统中工作(可能需要调用System.runFinalizersOnExit(true),从而确保这一行为)。

在一个特定的作用域内,若一个对象必须清除(非由垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有finally从句的try块,开始清除工作。

若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。

7.2.12. 使用数组传参

创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编译期进行类型检查的好处。此外,为使用它们,数组的接收者也许并不需要将对象“造型”到数组里。

7.2.13. 抽象类与接口的选择

尽量使用interface,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。

7.2.14. 源文件保存

为避免编程时遇到麻烦,请保证在自己类路径指到的任何地方,每个名字都仅对应一个类。否则,编译器可能先找到同名的另一个类,并报告出错消息。若怀疑自己碰到了类路径问题,请试试在类路径的每一个起点,搜索一下同名的.class文件。

火龙果 整理 uml.org.cn