Open fWX228941 opened 5 years ago
每个人的编码习惯和代码风格都不尽相同,不说那些缺乏良好编码习惯的开发人员,就连那些已经养好良好编码习惯的人员,很多方面都会存在差异,如若不约定好统一规范,久了肯定乱,尤其是在团队开发中非常明显,因此提前定义好规范,统一编程风格是必须的,接下来我将以优先级的次序依次展开详解。
好的命名,表词达意,意图明显,阅读起来干净利落,一目了然,赏心悦目,甚至都不需要查看多余的注释,即使新手也可迅速上手,其重要程度使我将其优先级直接提到了最高。
2.1.通用原则:
1.1..命名尽量意思表达准确,用全英文名,避免缩写。
以变量名为例(同样适用于为类,包,文件以及其他的编程实体命名)取名要完全,准确地描述出该变量所代表的事物,可读易懂,这就是最佳的变量名,避免晦涩难懂的缩写,请善用百度翻译。 比如:ListView 有人喜欢缩写为lv,TextView缩写为tv,含糊不清,是很不好的缩写方式。 比如:要表达“当前日期”,好的命名“currentDate” 直白明了,坏的命名“current 不知道当前是什么”,“date 表示所有日期,并非特指当前日志”。
1.2.名字长度:可以长但绝不允许太短,确保名字含义足够清晰。
短的变量名作用于局部变量和循环变量,比如i/j/k常常用作于循环下标或者数组下标,代表一个临时数据,作用域也非常有限,单循环内的变量约定俗成i,j,k,深入人心,我们也一并继承过来, 循环外或者多嵌套循环变量需要取一个更有意义的名字。
1.3.计算量的限定词,请放在修饰名的后面,Total,Sum,Average,Max,Min。这样是为了把变量名中最重要的部分放在最前面。
比如:要表达“总支出”expenseTotal>totalExpense 尽管语义上是等价的。
1.4.缩写策略:还是建议全写,适当的去掉每个单词的几个字母,保留每个音节中最引人注意的发音,避免歧义。
2.2.包名:包名具有框架级的意义,不仅可以描述出此包的核心功能还有代码路径,如若没有特殊说明此后的项目统一以com.caltta.xx的方式命名,包名要求全部小写。
2.3.类名:类是封装相关操作与属性的集大成者,其命名以功能名+组件名的方式,功能名尽量全写,除非单词特别长可适当缩写,首字母要求大写,类名包含的逐个单词其第一个字母大写,其他部分小写(驼峰方式)。
2.4.资源文件名:以功能名_包名的方式统一命名,中间以_隔开,我们以eclipse为参考,使用android studio等其他开发工具的同上述原理,不再赘述。
2.5.方法名:全称以驼峰方式来命名,首字母要求小写,方法名包含的逐个单词其第一个字母大写,其他部分小写,并附上作者和注释。
2.6.变量名:成员/全局变量以m开头,局部/临时变量/参数小写开头全称驼峰即可,常量固定大写,所有非字面含义的数字以常量方式命名,不得出现magic number,并且要求写上注释,优先英文。 2.7 接口名称:命名以I开始。
3.1.在声明全局变量的时候初始化,临时变量在第一次使用的位置初始化
3.2计数器功能的临时变量以及声明为static的共享全局变量注意检查是否需要重新初始化。 成员变量其生命周期从new对象实例化开始到被GC回收为止,而static静态变量其生命周期与应用application保持一致,无视对象化,具有最长生命周期。
3.3.尽可能缩短变量的存活时间,缩小其作用域,当对变量的作用域犹豫不决时,请选择该变量所具有的最小的作用域,能用局部变量的地方避免使用全局变量。 局限于某个特定的循环 > 子程序 > private 成员变量 > protected 继承变量 > public 共享变量 > public static 全局变量
3.4.变量也遵循单一原则,即每个变量只用于单一用途,单职责,禁止单变量多用途,滥用会导致混合耦合。 同时确保使用了所有已经声明的变量,在eclipse中会自动检测从未被使用过的变量,为其贴上黄色标签。
4.1.单一职责:类的设计要遵循单一职责原则,即就一个类而言,应该仅有一个引起它变化的原因,至于如何划分并不总是那么清晰,很多时候需要靠个人经验来界定,这里提供一些原则供参考。
4.3.扩展开发:当业务需求发生变化时,应该尽量通过扩展的方式来扩展实现,而不是通过修改已有的代码来实现,除非是因为代码本身错误,即对软件中的对象(类,模块,函数)对扩展开放,对修改封闭。
4.4.继承:利用继承来设计基类,抽象出公共属性和方法,以此达到代码复用的程度,继承是一种高耦合的关系,颇具侵略性,因为只要继承就必须拥有父类的所有属性和方法,所以不是父子关系的两个类不要设计为继承关系,其判断依据为是不是“is a 是一个”的关系。
4.5.面向接口编程:最大化对外隐藏实现细节,类之间不发生直接的依赖关系,其依赖关系通过接口或者抽象类产生,并且是尽量最小化接口,最典型的就是观察者模式在模块(类之间)通信方面的应用,如果两个类之间直接依赖于细节,那么它们之间就有了直接的耦合,当具体实现发生变化时,意味着要同时修改依赖者的代码。
4.6.低耦合:一个类应该对自己需要耦合或调用的类知道得最少,服务端类的内部如何实现与调用者或者客户端类没有关系。
创建高质量的子程序的一些指导原则:
5.1.子程序是一个为实现特定目的而编写的一个可被调用的方法,把一段功能实现放入一个命名恰当的方法中,能提供更高层次的抽象,隐藏实现细节,
5.2.当子程序内部循环或条件判断的嵌套层次过深时(3次),或者子程序的行数过长(200行),业务复杂时,也需要把嵌套的部分提取出来形成一个独立的子程序,以降低复杂度。
5.3.避免代码重复,通过使用子程序,只需要在一处地方优化代码,提高代码性能。
5.4.功能简单的小程序也是有必要的,因为简单逻辑随着业务的增大,存在潜在的复杂性,可扩展性,而且可读性也增强了。
5.5.子程序不是任意的拼装组合,是一组相关性的操作集合,要求子程序执行的操作与其名字相符,职责单一,如果它还做了其他的事情,那么它就不够内聚,命名也有问题,让每一个子程序只把一件事情做好,这样的子程序才健壮稳定,
5.6.对于参数,用临时变量对其加以引用,把参数的输入值赋值给工作的临时变量,这种方式指向性强,强调了数据来自何方,同时也能避免对于参数表中变量的值的意外修改。 参数个数不宜过多,限制在7个以内,传递的参数越多,说明子程序之间的耦合越紧,这同时也是一个信号,提示你应该考虑重新设计这个子程序。
6格式
7.1.避免重复性注释,注释要简洁准确,用注释来指明代码的意图。
7.2.如果代码过于复杂而需要解释,最好改进代码,使代码清晰后再用概述性注释或者意图性的注释,如果需要写很多注释,则需要考虑是否需要对代码本身的可读性下功夫。
7.3.注释不是重复代码内容,注释的意义在于包含了代码中没有的信息,同时还存在一种场景是代码其本身有些含义未表达明显,就把这写含义放在注释里,可以通过如果将这段代码换成同样功能的子程序,怎样命名的方式来写注释。
7.4变量注释 1)成员变量的声明时,添加必要的行尾注释。 2)变量声明的注释应给出变量名未表达出来的各种信息。 3)如果变量值有一个期望范围,注释应该说明。
7.5.控制结构注释 1)如果是while/for等循环语句,则指出这个循环的目的是什么,注释放在循环结 尾处。 2)如果是if/case等判断语句,则提供为什么要进行判断的理由,以及执行结果的一个总结,注释放在代码块上面。
7.6子程序注释 1)原则上所有子程序都应附带概述性的说明,除非已有很简单的Get/Set访问器子程序。 2)子程序的注释通常一两句就可以说清楚,过多甚至超过了代码长度是有问题。 3)对于子程序中输入输出的参数说明,如果子程序短,并且通过参数名和返回值名很清晰的区分理解,则不用注释说明,否则尤其是提供服务端性质的子程序应该为每个参数和返回值做说明,并且附带必要的作者信息。 4)对不清晰的依赖关系进行说明。
7.7.类注释 1)不要在类接口处说明实现细节,更好的封装性。
8.1.避免不同类型的值进行比较。 8.2.在含有复杂判断条件的if语句中,用一个布尔函数来对程序提供说明,大大提供可读性。 8.3.性能上考虑,android不推荐用emu 枚举类型,使用@IntDef @StringDef来代替 8.4.禁止使用magic number 使用具名常量代替,使用具名常量是一种将程序“参数化”的方法——把程序中可能变化的一个方面写为一个参数,当需要对其修改时,只改变一处就可以了。 8.5.对于数组类型,下标越界是常见错误,用arrayList这样的容器取代数组,检查数组的边界点,确认所有的数组下标没有超出数组的边界。
9语句 9.1.顺序相关的语句:如果语句之间存在依赖关系,并且这些关系要求你把语句按照一定的顺序加以排列,那么请设法使得这些依赖关系变得明显,不要过分无参封装成几个有依赖关系的方法。
9.2.顺序无关的语句:合理组织代码,要让程序易于自上而下阅读。
9.3.语句的相关性:所谓相关性是指它们都处理了相同的数据,执行了相似的任务,或者具有某种执行顺序上的依赖关系,因此要把相关的语句放在一起,这也是把它们重构成独立子程序的信号。
9.4.条件语句: 1)把主流程场景放在if后面处理,异常处理或者不常见场景放在else后面,即把决策的结果代码放在尽可能靠近决策位置。 2)避免空的if语句,简单地对if语句中的谓词作否定!boolean,把else子句中的代码移到if子句中来,并且去掉else子句就可以了。 3)对于多个判断if else语句,把最常见的情况放在最前面。确保所有的场景都考虑到,最后的else 语句用来捕获异常和你没有考虑的情况,这种消息是给程序员而非最终用户看的。 4)对于switch case语句按执行频率排列case子句,把正常的情况放在前面,case语句适用于简单的操作,如果是复杂的操作请封装成一个子程序,default子语句来检查错误。 5)if嵌套语句不要超过3次,一个有效减少嵌套的手段是使用return语句。在某些子程序中,一旦知道了答案,那么就使用return语句来返回到调用方子程序。
9.5.循环语句: 1)把循环看做是一个黑盒子,外围程序只知道它的控制条件,却不知道它的内容,需要把初始化的代码紧放在循环前面,避免分散在各处。 提前退出循环:break,程序会从循环后面的第一条语句开始执行下去。而continue是if else的一种缩写,阻止循环体剩余部分的执行而从循环的下一次迭代的开始位置继续执行。
10.1.谨慎使用try catch finally来捕获异常,仅仅在真正异常的情况下,即编码无法解决的情况下才使用异常机制,如果某种的错误场景可以在局部处理,就应该在局部处理掉它,异常机制只处理那些不仅罕见甚至永远都不该发生的情况,不应该用于正常的控制流。 10.2.优先使用标准异常
Checked Exception 属于非运行时异常,必须添加try catch 或者throw异常。
最重要的并不在于规范怎么定义,而是在于规范的严格执行。如果规范定义好了,但却不遵守,那规范就等于形同虚设,因此规范一旦设定,就要严格执行。
1前言
每个人的编码习惯和代码风格都不尽相同,不说那些缺乏良好编码习惯的开发人员,就连那些已经养好良好编码习惯的人员,很多方面都会存在差异,如若不约定好统一规范,久了肯定乱,尤其是在团队开发中非常明显,因此提前定义好规范,统一编程风格是必须的,接下来我将以优先级的次序依次展开详解。
2命名规范
好的命名,表词达意,意图明显,阅读起来干净利落,一目了然,赏心悦目,甚至都不需要查看多余的注释,即使新手也可迅速上手,其重要程度使我将其优先级直接提到了最高。
2.1.通用原则:
以变量名为例(同样适用于为类,包,文件以及其他的编程实体命名)取名要完全,准确地描述出该变量所代表的事物,可读易懂,这就是最佳的变量名,避免晦涩难懂的缩写,请善用百度翻译。 比如:ListView 有人喜欢缩写为lv,TextView缩写为tv,含糊不清,是很不好的缩写方式。 比如:要表达“当前日期”,好的命名“currentDate” 直白明了,坏的命名“current 不知道当前是什么”,“date 表示所有日期,并非特指当前日志”。
短的变量名作用于局部变量和循环变量,比如i/j/k常常用作于循环下标或者数组下标,代表一个临时数据,作用域也非常有限,单循环内的变量约定俗成i,j,k,深入人心,我们也一并继承过来, 循环外或者多嵌套循环变量需要取一个更有意义的名字。
比如:要表达“总支出”expenseTotal>totalExpense 尽管语义上是等价的。
2.2.包名:包名具有框架级的意义,不仅可以描述出此包的核心功能还有代码路径,如若没有特殊说明此后的项目统一以com.caltta.xx的方式命名,包名要求全部小写。
2.3.类名:类是封装相关操作与属性的集大成者,其命名以功能名+组件名的方式,功能名尽量全写,除非单词特别长可适当缩写,首字母要求大写,类名包含的逐个单词其第一个字母大写,其他部分小写(驼峰方式)。
2.4.资源文件名:以功能名_包名的方式统一命名,中间以_隔开,我们以eclipse为参考,使用android studio等其他开发工具的同上述原理,不再赘述。
2.5.方法名:全称以驼峰方式来命名,首字母要求小写,方法名包含的逐个单词其第一个字母大写,其他部分小写,并附上作者和注释。
2.6.变量名:成员/全局变量以m开头,局部/临时变量/参数小写开头全称驼峰即可,常量固定大写,所有非字面含义的数字以常量方式命名,不得出现magic number,并且要求写上注释,优先英文。 2.7 接口名称:命名以I开始。
3变量
3.1.在声明全局变量的时候初始化,临时变量在第一次使用的位置初始化
3.2计数器功能的临时变量以及声明为static的共享全局变量注意检查是否需要重新初始化。 成员变量其生命周期从new对象实例化开始到被GC回收为止,而static静态变量其生命周期与应用application保持一致,无视对象化,具有最长生命周期。
3.3.尽可能缩短变量的存活时间,缩小其作用域,当对变量的作用域犹豫不决时,请选择该变量所具有的最小的作用域,能用局部变量的地方避免使用全局变量。 局限于某个特定的循环 > 子程序 > private 成员变量 > protected 继承变量 > public 共享变量 > public static 全局变量
3.4.变量也遵循单一原则,即每个变量只用于单一用途,单职责,禁止单变量多用途,滥用会导致混合耦合。 同时确保使用了所有已经声明的变量,在eclipse中会自动检测从未被使用过的变量,为其贴上黄色标签。
4类
4.1.单一职责:类的设计要遵循单一职责原则,即就一个类而言,应该仅有一个引起它变化的原因,至于如何划分并不总是那么清晰,很多时候需要靠个人经验来界定,这里提供一些原则供参考。
4.3.扩展开发:当业务需求发生变化时,应该尽量通过扩展的方式来扩展实现,而不是通过修改已有的代码来实现,除非是因为代码本身错误,即对软件中的对象(类,模块,函数)对扩展开放,对修改封闭。
4.4.继承:利用继承来设计基类,抽象出公共属性和方法,以此达到代码复用的程度,继承是一种高耦合的关系,颇具侵略性,因为只要继承就必须拥有父类的所有属性和方法,所以不是父子关系的两个类不要设计为继承关系,其判断依据为是不是“is a 是一个”的关系。
4.5.面向接口编程:最大化对外隐藏实现细节,类之间不发生直接的依赖关系,其依赖关系通过接口或者抽象类产生,并且是尽量最小化接口,最典型的就是观察者模式在模块(类之间)通信方面的应用,如果两个类之间直接依赖于细节,那么它们之间就有了直接的耦合,当具体实现发生变化时,意味着要同时修改依赖者的代码。
4.6.低耦合:一个类应该对自己需要耦合或调用的类知道得最少,服务端类的内部如何实现与调用者或者客户端类没有关系。
5子程序
创建高质量的子程序的一些指导原则:
5.1.子程序是一个为实现特定目的而编写的一个可被调用的方法,把一段功能实现放入一个命名恰当的方法中,能提供更高层次的抽象,隐藏实现细节,
5.2.当子程序内部循环或条件判断的嵌套层次过深时(3次),或者子程序的行数过长(200行),业务复杂时,也需要把嵌套的部分提取出来形成一个独立的子程序,以降低复杂度。
5.3.避免代码重复,通过使用子程序,只需要在一处地方优化代码,提高代码性能。
5.4.功能简单的小程序也是有必要的,因为简单逻辑随着业务的增大,存在潜在的复杂性,可扩展性,而且可读性也增强了。
5.5.子程序不是任意的拼装组合,是一组相关性的操作集合,要求子程序执行的操作与其名字相符,职责单一,如果它还做了其他的事情,那么它就不够内聚,命名也有问题,让每一个子程序只把一件事情做好,这样的子程序才健壮稳定,
5.6.对于参数,用临时变量对其加以引用,把参数的输入值赋值给工作的临时变量,这种方式指向性强,强调了数据来自何方,同时也能避免对于参数表中变量的值的意外修改。 参数个数不宜过多,限制在7个以内,传递的参数越多,说明子程序之间的耦合越紧,这同时也是一个信号,提示你应该考虑重新设计这个子程序。
6格式
7注释
7.1.避免重复性注释,注释要简洁准确,用注释来指明代码的意图。
7.2.如果代码过于复杂而需要解释,最好改进代码,使代码清晰后再用概述性注释或者意图性的注释,如果需要写很多注释,则需要考虑是否需要对代码本身的可读性下功夫。
7.3.注释不是重复代码内容,注释的意义在于包含了代码中没有的信息,同时还存在一种场景是代码其本身有些含义未表达明显,就把这写含义放在注释里,可以通过如果将这段代码换成同样功能的子程序,怎样命名的方式来写注释。
7.4变量注释 1)成员变量的声明时,添加必要的行尾注释。 2)变量声明的注释应给出变量名未表达出来的各种信息。 3)如果变量值有一个期望范围,注释应该说明。
7.5.控制结构注释 1)如果是while/for等循环语句,则指出这个循环的目的是什么,注释放在循环结 尾处。 2)如果是if/case等判断语句,则提供为什么要进行判断的理由,以及执行结果的一个总结,注释放在代码块上面。
7.6子程序注释 1)原则上所有子程序都应附带概述性的说明,除非已有很简单的Get/Set访问器子程序。 2)子程序的注释通常一两句就可以说清楚,过多甚至超过了代码长度是有问题。 3)对于子程序中输入输出的参数说明,如果子程序短,并且通过参数名和返回值名很清晰的区分理解,则不用注释说明,否则尤其是提供服务端性质的子程序应该为每个参数和返回值做说明,并且附带必要的作者信息。 4)对不清晰的依赖关系进行说明。
7.7.类注释 1)不要在类接口处说明实现细节,更好的封装性。
8类型对象和数据结构
8.1.避免不同类型的值进行比较。 8.2.在含有复杂判断条件的if语句中,用一个布尔函数来对程序提供说明,大大提供可读性。 8.3.性能上考虑,android不推荐用emu 枚举类型,使用@IntDef @StringDef来代替 8.4.禁止使用magic number 使用具名常量代替,使用具名常量是一种将程序“参数化”的方法——把程序中可能变化的一个方面写为一个参数,当需要对其修改时,只改变一处就可以了。 8.5.对于数组类型,下标越界是常见错误,用arrayList这样的容器取代数组,检查数组的边界点,确认所有的数组下标没有超出数组的边界。
9语句 9.1.顺序相关的语句:如果语句之间存在依赖关系,并且这些关系要求你把语句按照一定的顺序加以排列,那么请设法使得这些依赖关系变得明显,不要过分无参封装成几个有依赖关系的方法。
9.2.顺序无关的语句:合理组织代码,要让程序易于自上而下阅读。
9.3.语句的相关性:所谓相关性是指它们都处理了相同的数据,执行了相似的任务,或者具有某种执行顺序上的依赖关系,因此要把相关的语句放在一起,这也是把它们重构成独立子程序的信号。
9.4.条件语句: 1)把主流程场景放在if后面处理,异常处理或者不常见场景放在else后面,即把决策的结果代码放在尽可能靠近决策位置。 2)避免空的if语句,简单地对if语句中的谓词作否定!boolean,把else子句中的代码移到if子句中来,并且去掉else子句就可以了。 3)对于多个判断if else语句,把最常见的情况放在最前面。确保所有的场景都考虑到,最后的else 语句用来捕获异常和你没有考虑的情况,这种消息是给程序员而非最终用户看的。 4)对于switch case语句按执行频率排列case子句,把正常的情况放在前面,case语句适用于简单的操作,如果是复杂的操作请封装成一个子程序,default子语句来检查错误。 5)if嵌套语句不要超过3次,一个有效减少嵌套的手段是使用return语句。在某些子程序中,一旦知道了答案,那么就使用return语句来返回到调用方子程序。
9.5.循环语句: 1)把循环看做是一个黑盒子,外围程序只知道它的控制条件,却不知道它的内容,需要把初始化的代码紧放在循环前面,避免分散在各处。 提前退出循环:break,程序会从循环后面的第一条语句开始执行下去。而continue是if else的一种缩写,阻止循环体剩余部分的执行而从循环的下一次迭代的开始位置继续执行。
10 错误处理
10.1.谨慎使用try catch finally来捕获异常,仅仅在真正异常的情况下,即编码无法解决的情况下才使用异常机制,如果某种的错误场景可以在局部处理,就应该在局部处理掉它,异常机制只处理那些不仅罕见甚至永远都不该发生的情况,不应该用于正常的控制流。 10.2.优先使用标准异常
Checked Exception 属于非运行时异常,必须添加try catch 或者throw异常。
11结语
最重要的并不在于规范怎么定义,而是在于规范的严格执行。如果规范定义好了,但却不遵守,那规范就等于形同虚设,因此规范一旦设定,就要严格执行。