AirJD 焦点
AirJD

没有录音文件
00:00/00:00
加收藏

Java代码规范:一致的风格比“正确”的风格更重要 by PersiaCai

发布者 style
发布于 1435044736079  浏览 8788 关键词 Java 
分享到

第1页

Code Style

PersiaCai 2014-07-18



第4页

一致的风格比“正确”的风格更 重要



第5页

目录

 Why Code Style  What’s Code Style about  How to follow



第6页

WHY

1、业界约定俗成

2、代码结构美学 3、工程化的需要



第7页

WHAT

1、Specification(根据GoogleCodeStyle)

 源文件及其结构  格式及注释  声明及命名

2、Best Practice 方法、命名



第8页

1.1源文件及其结构

1、编码格式:UTF-8 2、换行:UNIX/LINUX 换行 (git设置) 3、import,不使用通配符 4、类成员顺序:

A、public – protected – private B、类成员在上,方法在下



第9页

2.1格式—括号



1、可选大括号也加上

if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把 大括号写上

2、Kernighan和Ritchie风格



左大括号前不换行 左大括号后换行 右大括号前换行



if (condition()) { try { something(); } catch (ProblemException e) { recover(); }

}



第10页

2.2格式—注释

1、字段 /** xxx */ 2、方法

/** * */ public void push(PaintBoard paintBoard) {

doSomething(); }



第11页

2.2格式--注释

3、好代码>坏代码+注释 4、记录你的思想

//这个用法是出于…考虑



第12页

2.3格式-缩进

1、语句块缩进

每当一个新的语句块产生,缩进就增加两个空格。 当这个语句块结束时,缩进恢复到上一层级的缩进格数。

2、每句代码的结束都需要换行。 3、行长度限制:80或100

长行要断行,断行缩进4字符



第13页

2.4格式-空白

1、垂直空白

方法间空白行间隔 方法体类空白行进行逻辑分组 类成员之间,可以不空白行间隔

2、水平空白

赋值等号前后的空白间隔



第14页

3.1声明

1、每次声明一个变量

不要采用一个声明,声明多个变量。例如 int a, b;

2、不能像C风格一样声明数组

应该是String[] args,而不是 String args[]

3、修饰符顺序

public protected private abstract static final transient volatile synchronized native strictfp



第15页

3.2命名

1、标示符只应该使用ASCII字母、数字和下划 线,字母大小写敏感

尽量不使用前后缀,比如name_, s_name

2、包名全小写,并不使用下划线 3、类名

class命名一般使用名词或名词短语。interface的命名有时也可以使用形容词或形容词 短语;测试类以Test结尾;接口不加I,实现类Impl结尾



第16页

3.2命名

4、方法名

方法命名一般使用动词或者动词短语,在JUnit的测试方法中,可以使用下划线,用来 区分测试逻辑的名字经常使用如下的结构:test<MethodUnderTest>_<state> 。例如: testPop_emptyStack 。

5、常量

大写加下划线分隔;一般使用名词或者名词短语命名。

6、变量

非常量的成员变量命名(包括静态变量和非静态变量),采用lowerCamelCase命名; Camel case 命名:XMLHTTPRequest改为XmlHttpRequest

7、参数名

应该避免使用一个字符作为参数的命名方式。



第17页

Best Practice



第18页

可读性基本原理

代码的写法应当使别人理解它的 时间最小化



第19页

一、命名

1、提防使用不同之处较小的名称

XYZControllerForEfficientHandlingOfStrings 与 XYZControllerForEfficientStorageOfStrings 在IDE自动补全,容易补全错。

2、使用可搜索的名称

变量作用域大的可以命名相对长一些,便于搜索。

3、每个概念对应一个词

fetch、retrieve、get等若同时出现,容易造成混乱,尽量统一只用一个词。



第20页

一、命名

4、布尔值

经常以is开头后面跟名称或名称短语 isDigit,isProbablePrime,isEmpty,isEnabled,hasSiblings 缺点是降低逻辑表达式可读性:if isEmpty (if empty)

5、类型转换

使用toType,比如toString,toArray

6、返回视图

使用asType,比如asList

7、返回基本类型

使用typeValue,比如intValue

8、静态工厂

经常使用valueOf,of,getInstance,newInstance,getType,newType



第21页

一、命名

9、避免泛泛的词

downloadPage()要优于getPage() countSize(),calculateSize()要优于getSize(),size(),前者表示重量级的,需要消

耗时间 maxChars要优于maxLength



第22页

一、命名

10、用min,max表示上下限

minInputSize,maxInputSize

11、first,last表示包含的范围

firstDate,lastDate

12、begin,end表示包含-排除的范围

beginDate,endDate

13、动宾结构

描述好所做的全部事情 printDocument, calcMonthlyRevenues 描述好返回值 printer.isReady()

14、计算值限定词加到名字最后

revenueTotal、revenueAverage这样比totalRevenue、averageRevenue更容易 看出命名含义、更有规则性



第23页

一、命名

15、常用对仗词

begin/end、first/last、locked/unlocked、min/max、next/previous、old/new、 opened/closed、visible/invisible、source/target、up/down

15、缩写的一般指导原则

去掉所有非前置元音(computer->cmptr,apple->appl) 使用每个单词的第一个或前几个字母 保留每个单词的第一个和最后一个字母 去除无用的后缀(ing、ed等)



第24页

二、函数

1、函数参数越少越好

调用方不用了解细节

2、同类型函数参数最好间隔

不间隔容易传错而不自知

3、避免out型参数

容易引起混乱,统一在返回值里返回

4、避免boolean参数

调用方不知道啥意思,采用枚举替代

5、超过4个参数最好封装为类

同时增加precondition校验



第25页

二、函数



6、参数避免传null



null值意义不明确

7、返回值避免传null



集合类型返回EMPTY_LIST/EMPTY_MAP/EMPTY_SET, EMPTY_ARR?

8、分隔指令与询问



函数要做什么,要回答什么,二者不可兼得。 只做一件事情,提供功能内聚性。



if(set("username","unclebob")){ ...

}



if(attributeExists("username")){ setAttribute("username","unclebob");

}



第26页

二、函数

9、异常代替错误码

错误码常常暗示这枚举,应变性差,容易造成if嵌套,改用exception,错误处理就能从 主路径代码中分离出来,得到简化。

10、不要使用异常控制循环体



for(Item item : items){ try{ doSomething(item); }catch(Exception e){ continue; }

}



try{ Iterator<Foo> i = collection.iterator(); while(true){ Foo foo = i.next(); }

}catch(NoSuchElementException e){ }



第27页

二、函数

11、分离try/catch的主体

抽出方法,简洁

12、使用卫述句

void compute(){ Server server=getServer(); if(server==null) return; Client client=server.getClient(); if(client==null) return; Request current=client.getRequest(); if(current==null) return; processRequest(current);

}



第28页

二、函数

13、容器成员提供额外封装

封装增加或删除元素方法,对于返回整个容器,尽量返回不可变类型

List<Book> getBooks(){ return Collections.unmodifiableList(books);

}

void addBook(Book arrival){ books.add(arrival);

}

int bookCount(){ return books.size();

}

Iterator getBooks(){ return books.iterator();

}



第29页

二、函数

14、尽量避免连续调用

builder,stream等模式除外

predeal.getPlanTask().getProduct().getCoverImg();



第30页

二、函数

15、方法命名帮助传参

assertEquals(expected,actual),让调用者需要注意传入的顺序,有一定的犯错的概率。

assertExpectedEqualsActual(expected,actual),这样的函数命名,就大大减轻了记忆 参数顺序的负担。

16、描述化

//does the module from the global .... if(module.getDependSubsystems().contains(subSysMod.getSubSystem()))

List moduleDependees = module.getDependSubsystems(); String ourSubSystem = subSysMod.getSubSystem(); if(moduleDependees.contains(ourSubSystem ))



第31页

二、函数

17、防卫式校验

public Period(Date start, Date end) { if (start.compareTo(end) > 0) throw new IllegalArgumentException( start + " after " + end); this.start = start; this.end = end;

}

18、慎用可变参数

#wrong System.out.println(Arrays.asList(myArray)); #right System.out.println(Arrays.toString(myArray));



第32页

二、函数

19、for优先于while

Iterator<Element> i = c.iterator(); while(i.hasNext()){

doSomething(i.next()); }

for(Iterator<Element> i =c.iterator();i.hasNext();){ doSomething(i.next());

}

Iterator<Element> i2 = c2.iterator(); //BUG,拷贝出错了 while(i.hasNext()){

doSomething(i.next()); } //compile error for(Iterator<Element> i2 =c2.iterator();i.hasNext();){

doSomething(i.next());



第33页

二、函数

20、for-each优先于for

public class NestedIteration { public static void main(String[] args) { Collection<Suit> suits = Arrays.asList(Suit.values()); Collection<Rank> ranks = Arrays.asList(Rank.values());

List<Card> deck = new ArrayList<Card>(); //BUG,NoSuchElementException for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )

for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) deck.add(new Card(i.next(), j.next()));

// Preferred idiom for nested iteration on collections and arrays // for (Suit suit : suits) // for (Rank rank : ranks) // deck.add(new Card(suit, rank));

} }



第34页

二、函数

21、不要修改入参

public int sample(int inputVal){ inputVal = inputVal * 2; … return inputVal;

}

inputVal是入参,容易理解为不变的,后续修改者可能把它当做不变量用。

public int sample(int inputVal){ int workingVal= inputVal * 2; … return workingVal;

}



第35页

二、函数

22、异常的抽象层次应与方法一致

class Employee{ public TaxId getTaxId() throws EOFException{

} }

此处不应该把底层异常抛出去,暴露了实现细节,应该包装为与之在同一层次的异常

class Employee{ public TaxId getTaxId() throws EmployeeDataNotAvailable{

} }



第36页

二、函数

23、用布尔变量对程序加以文档说明

if( (elementIndex < 0) || (MAX_ELEMENTS < elementIndex) || (elementInddex == lastElementIndex) ){

}

目的不明确的布尔判断 VS 目的明确的布尔判断

finished = (elementIndex < 0) || (MAX_ELEMENTS < elementIndex) ; repeatedEntry = elementInddex == lastElementIndex ; If (finished || repeatedEntry){

}



第37页

函数

相对于追求最小化代码行数,一个更好的度量 方法是最小化人们理解它所需的时间。

if (exponent >= 0) { return mantissa * (1 << exponent);

} else { return mantissa / (1 << -exponent);

}

return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);



第38页

三、循环

1、在写一个比较时 (while(bytes_expected>bytes_received)

), 把改变的值写在左边并且把更稳定的值写在

右边更好一些(while(bytes_received< bytes_expected))。



第39页

三、循环

2、什么时候使用while,什么时候使用for

 while是灵活的循环,适用于预先不知道循环要迭 代多少次;

 for循环用来执行那些不需要循环内部控制的简单 操作,适合循环控制就是简单的递增或递减,无 须在循环内部去控制循环条件。

 尽量不要在for循环通过直接修改下标值的方式迫 使它终止。



第40页

三、循环



3、带退出循环,更容易维护



while(true){ if(rating < target){ break; } rating = cal(parm);

}



rating = cal(param); while(rating < target){

rating = cal(param); }

重复的代码,在修改的时 候,常常忘记保持一致



第41页

三、循环

4、循环内务操作尽量放到循环体内,循环头 仅仅控制条件

while((inputChar = dataFile.getChar()) != EOF_CHAR){ ;

}

do{ inputChar = dataFile.getChar();

} while( inputChar != EOF_CHAR)



第42页

三、循环

4、误用for循环

for(inputFlie.moveToStart(),recordCount =0; !inputFile.endOfFile(); recordCount++){ inputFile.getRecord();

}

For循环头的位置保留给循环控制语句。

inputFile.moveToStart(); recordCount=0; while(!inputFile.endOfFile()){

inputFile.getRecord(); recordCount++; }



第43页

单元测试

(1)快速(Fast)

快速运行

(2)独立(Independent)

每个测试相互独立,某个测试不应该为下一个测试的设定条件

(3)可重复(Repeatable)

在任何环境可以重复通过

(4)自足验证(Self-Validating)

测试应该有布尔值输出,无论是通过还是失败,不应该通过日志文件来确认测 试是否通过。

(5)及时(Timely)

在编写代码之前编写测试



第44页

单元测试

1、一个单元测试是一段代码,这段代码调用一个工作单元,并检验该工作单元 的一个具体的最终结果。如果关于这个最终结果的假设是错误的,单元测试 就失败了。一个单元测试的范围可以小到一个方法,大到多个类。

2、集成测试是对一个工作单元进行的测试,这个测试对被测试的工作单元没有 完全的控制,并使用该单元的一个或多个真实依赖物,例如时间、网络、数 据库、线程或随机数产生器等。

3、单元测试方法的命名: [UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior]

实例:isValidFileName_badExtension_returnFalse() isValidFileName_goodExtensionLowercase_returnsTrue()

4、一个测试通常包括Arrange、Act和Assert这3部分,分别表示测试准备、调用 带测方法、验证结果是否符合预期。

5、在一个测试中先写最后的Assert部分,然后推导出Act和Arrange这两部分代码 ,会更加符合TDD的测试驱动风格,营造出分形的和谐气氛。



第45页

单元测试臭味

1、强制的测试顺序 不能保证测试的可重复性

2、测试方法中调用其他测试方法 破坏了测试的独立性

3、破坏共享状态 破坏了测试的稳定性



第46页

HOW



第47页

Reference

Google Java Code Style



第48页

Reference



第49页

Reference



第50页

Q A



支持文件格式:*.pdf
上传最后阶段需要进行在线转换,可能需要1~2分钟,请耐心等待。