Clean Code Notes(代码规范)
/* edit by Vaycent on 2016/03/22
Pls feel free to contact, vaycentsun@gmail.com
*/
[TOC]
Clean Code Notes
1_命名
1.1_命名要点
- 有意义的命名
- 有意义的区分
- 有意义命名部分尽可能短,不要添加无意义的语境
- 可读
- 可搜索
1.1.1_类名与对象名
使用名词或名词短语,如Customer、WikiPage、Account、AddressParser
类名使用每单词首字母大写,如AccountGroup、FoodRecords
1.1.2_方法名
使用动词或动词短语,如postPayment、deletePage、使用前缀get、set、is等
变量名/方法名使用首单词小写,其余单词首字母大写,如getMedicationAlarm、bpMeasurementValue
重载/重构(overload):同一个类中,同名方法,但参数不同,Java会将其视为唯一方法
重写(override):子类继承父类后,在参数与返回值相同的前提下,重新定义方法体
重载时(overload),使用描述了该参数静态方法,比直接New该参数的方法要好,例如:
Complex fulcrumPoint = Complex.fromRealNumber(23.0);
通常好于
Complex fulcrumPoint = New Complex(23.0);
2_函数(方法)
2.1_函数要点
- 短小
- 少参数
- 只处理一个抽象层级
- 只做一件事,
- 异常代替错误码
短小:函数尽可能短小、简洁、结构清晰
少参数:尽可能减少输入的参数。尽可能不使用boolean值作为输入,若有则进行拆解,如:
Boolean isSuite;
//...
render(isSuite);
//拆分成 ====>>
//...
renderForSuite();
renderForSingleTest();
只处理一个抽象层级:避免让读者混淆,无法判断内容是基础概念还是细节,如不在函数里同时处理版面和内容
只做一件事:在保证同一个抽象层级的前提下,函数只负责做一个步骤(一件事)
异常代替错误码:错误码通常用一个类(class)或者枚举(enum)来定义,其他许多类都得导入或者使用它。但当以后修改的时候,就需要重新构建或部署。使用异常来代替错误码,新异常可以继承基类,派生出来,无需重新部署。
3_注释
3.1_注释要点
- 辅助信息
- 传达作者的意图
- TODO注释
- Javadoc注释
- 法律信息
- 警示
3.1.1_辅助信息
为函数、变量、类等给予辅助解释,提供基本信息。这类注释若能通过函数、变量名字的方式传达信息的话,能避免就避免使用。例如:
//Returns an instance of the Responder being tested.
protected abstract Responder responderInstance();
这个注释就不如使用responderBeingTested()作为函数名
3.1.2_传达作者的意图
在函数名、变量名能提供足够要传达信息的前提下,注释用来描述作者此段代码的意图为更佳,而不是仅仅用于解释变量、函数代表的意义。例如在一段算法的注释当中,解释作者的思路,而不是解释变量名代表的意义。
3.1.3_TODO注释
TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作。它可能是提醒删除某个不必要的特性、或要求他人注意某个问题、或恳请别人取个好名字、或提示对依赖于某个计划事件的修改,等等...大多数IDE都提供了特别的手段来定位TODO,以Android Studio为例:
[图片上传中。。。(1)]
3.1.4_Javadoc注释
注释可用于编写描述Project的Document介绍,以/** */包括为内容,以p为段落,以br为换行,以@param为输入参数,以@return为输出参数。以下Android Studio代码为例:
- 在Tools中选择Generate JavaDoc
- 修改Other command line argument(自己sdk下android.jar路径),-bootclasspath "/Users/smartone_sn2/Documents/android_SDK6.0_for_Studio/android-sdk-macosx/platforms/android-23/android.jar"
- 中文解释需用encode,与android.jar之间用空格隔开,-encoding utf-8 -charset utf-8
/** * 从url链接中获取特定parameter参数值.<p> * ... (方法的详细说明第一行)<br> * ... (方法的详细说明第二行)<br> * ... (方法的详细说明第三行) * @param url 作用于的url * @param parameter 要获取的参数名 * @return 特定参数的值 */protected static String getUrlParam(String url, String parameter) {String returnParameter = ""; if (url.contains("?")) {String paramUrl = url.substring(url.indexOf("?") + 1); String[] paramStrs = paramUrl.split("&"); for (int i = 0; i < paramStrs.length; i++) {if (paramStrs[i].contains(parameter)) {returnParameter = paramStrs[i].substring(paramStrs[i].indexOf("=") + 1); i = paramStrs.length - 1; }}}return returnParameter;}
[图片上传中。。。(2)]
3.1.5_法律信息
公司代码规范要求编写与法律有关的注释,如版权、著作权声明。注意这类注释不是合同和法典,只要有可能,就指向一份外部的标准许可或其他文档。例如:
//Copyright (c) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
//Released under the terms of the GUN General Public License version 2 or later.
3.1.6_警示
用于警告其他程序猿运行后会出现某种后果,或用来表示测试用例。例如下面注释解释了为何要关闭某个特定的测试用例:
//Don't run unless you have some time to kill
public void testWithReallyBigFile(){
writeLinesToFile(10000000);
response.setBody(testFile);
...
}
4_格式
4.1_垂直格式
- 利用空行,标识(分隔)出独立区域
- 实体、全局、静态变量集中在类的顶部声明
- 函数变量集中在函数顶部声明(函数分割得足够简短为前提)
- 相关概念的代码集中。或函数难以分割得简短,函数变量可不在顶部声明
- 相关函数集中相邻,如调用者在被调用者上方
4.2_水平格式
- 在嵌套中使用缩进
- 将紧密相关事物分割开**,如int lineSize = line.length();在赋值符号两侧空格
- 将运算优先级分割,如return b*b - 4*a*c;
但IDE的自动Format功能一般会消除这些格式
5_面向过程与面向对象
引言
Java为面向对象的语言,但同样能使用“数据结构+过程”的面向过程式地编程。首先,“面向对象”并不一定比“面向过程”更加优越,两种方式在不同情景中各有优劣。面向过程更善于添加新函数,分析思想是拿到问题后,拆分成逐个步骤解决,善于分析一个不完整的事物问题。面向对象更善于添加新类,分析思想是经过整体分析自顶而下,定于每个对象再组装起来,善于分析一个完整的事物问题。
5.1_抽象
抽象:指把现实世界中某一样事物,提取出描述点,用代码形式表示。抽象一般不打算了解该事物的全部描述点,只关注与目标有关的方面。抽象出来的叫做类或接口(Java中),抽象按方式划分可分为:
- 数据抽象
- 行为抽象
5.1.1_数据抽象(数据结构)
用代码形式抽象出参数(事物特征),如抽象出正方形有锚点x坐标、锚点y坐标、边长等特征
public class Square{
public Point topLeft;
public double side;
}
public class Rectangle{
public Point topLeft;
public double height;
public double width;
}
public class Circle{
public Point center;
public double radius;
}
public class Geometry{
public final double PI = 3.14;
public double area(Object shape) throws NoSuchShapeException{
if(shape instanceof Squre){
Square s = (Square)shape;
return s.side * s.side;
}else if(shape instanceof Rectangle){
Retangle s = (Retangle)shape;
return s.height * r.width;
}else if(shape instanceof Circle){
Circle s = (Circle)shape;
return PI * s.radius * s.radius;
}
throw new NoSuchShapeException();
}
}
//要用时,需要取出Square、Rectangle、Circle的公有参数,定义方法体来使用
5.1.2_行为抽象(对象)
用代码形式抽象出函数(事物方法),如抽象出正方形有获取锚点坐标、周长、面积等方法
public class Square implements Shape{
private Point topleft;
private double side;
public double area(){
return side * side;
}
}
public class Retangle implements Shape{
private Point topLeft;
private double height;
private double width;
public double area(){
return height * width;
}
}
public class Circle implements Shape{
private Point center;
peivate double radius;
public final double PI = 3.14;
public double area(){
return PI * radius * radius;
}
}
//要用时,直接使用Square.area()、Retangle.area()、Circle.area()
5.2_数据结构与对象的优劣对比
从以上对正方形、长方形、圆形面积的2种代码可看出,它们定义的本质是截然对立的,说明对象与数据结构是对同一事物,使用了完全不同的分析方法。
-
若此时需要添加一个功能,计算图形的面积是否大于100,若大于就返回100,则:
数据结构:在Geometry中直接加入判断即可
对象:需要分别在各个类中分别加入判断,控制输出的值,甚至有时需要改动更上一层父类的内容。 -
若此时需要添加三角形周长(新类)的计算,则:
数据结构:不仅需要添加三角形的数据结构,Geometry类的area也不能用了,甚至调用Geometry的地方也要修改。
对象:直接添加三角形的类,调用周长函数即可。
所以可得
- 数据结构:过程式代码,没有明显行为,暴露数据,便于添加新行为(新函数),难以添加新数据结构(新类)
- 对象:面向对象式代码,暴露行为,隐藏数据,难以添加新行为(新函数),便于添加新对象类型(新类)
5.3_面向过程与面向对象的对比结论
结论:
- 面向过程:在Java中可以理解为“数据结构+过程”式的编程,改动函数时更方便
- 面向对象:即由对象组织起来的编程,改动类时更方便
- "一切都是对象"只是一个传说😂