第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