做网站的前端框架,做的网站客户拿去维违法,许昌市建设局网站,黄江做网站2017年开春#xff0c;阿里对外公布了「阿里巴巴Java开发手册」从头到尾浏览了一遍这份手册之后#xff0c;感觉很棒。虽然其中的某些观点笔者不能苟同#xff0c;但大部分的规范还是值得绝大多数程序员学习和遵守的。 笔者将对这份代码规范中的一些细节做一些解读#xff…2017年开春阿里对外公布了「阿里巴巴Java开发手册」从头到尾浏览了一遍这份手册之后感觉很棒。虽然其中的某些观点笔者不能苟同但大部分的规范还是值得绝大多数程序员学习和遵守的。 笔者将对这份代码规范中的一些细节做一些解读包含笔者的观点和想法可以作为这份代码规范的扩展阅读。对于规范中某些「显而易见」的条款将不在解读范围之列换言之这都不懂就说明你天赋不够乘早别做程序员了。 当然笔者在日常的编程过程中属于「代码洁癖偏执狂」所以文中的某些观点仅代表个人看法请勿人生攻击。 阿里官方代码规范解读系列总计五篇已在本公众号发过本篇为合集对之前文章中的部分内容作了修订。 命名规约 1.1.1 代码中的命名均不能以下划线或美元符号开始也不能以下划线或美元符号结束1.1.2 代码中的命名严禁使用拼音与英文混合的方式更不允许直接使用中文的方式1.1.3 / 1.1.4 类名使用UpperCamelCase风格必须遵从驼峰形式某些情况诸如领域模型相关的命名除外方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格必须遵从驼峰形式1.1.5 常量命名全部大写单词间用下划线隔开1.1.9 包名统一使用小写点分隔符之间有且仅有一个自然语义的英语单词 上述规则主要是规定了你书写Java的时候哪些字符可以用什么时候用大写什么时候用小写。应该说绝大多数写Java的都遵循着上述的规范就像笔者说的尼玛这都不懂乘早改行别写Java了。 笔者在实际编程过程中对类名的风格可能更激进一些根据阿里的规范 类名使用UpperCamelCase风格必须遵从驼峰形式但以下情形例外领域模型的相关命名DO / BO / DTO / VO等 实际上还是有可能会存在着诸如UserVOUserDTOUserDAO这样的命名。对不起在笔者的团队中这样的命名也会被禁止这里分为2种情况 禁止使用 VO / BO / DTO / VO 等进行领域模型的命名 有读者要问那么如果万一项目中要使用DTO或者VO咋办笔者的观点如下 第一项目中避免使用DTO或者VODTO是一个早在2004年就被讨论并认定为一个反模式的东西 第二谁规定领域模型就一定要用DTO或者VO做结尾还原领域模型的本来意义才是命名的核心一个User在实际业务系统中可能是一个Admin或者Supervisor那就直接用Admin来命名而不是把User转化成UserVOUserVO啥都不是是初级程序员造出来的一个怪胎。 所有的DAO使用正常的驼峰法进行命名例如UserDao 对上面这条或许有很多DAO大写党要发飙了。其实笔者就想反问这些人一句你咋不把UserService写成UserSERVICE呢 命名原则 1.1.5 力求语义表达完整清楚不要嫌名字长1.1.10 杜绝完全不规范的缩写避免望文不知义 这两条说的是命名的基本原则总的来说其实表达了一个意思你他妈的别给我用缩写 其实有很多程序员会非常神奇的患上「缩写综合症」。比如非常典型的就是UserMgmt这他妈是什么鬼多敲几个字母会死么 类的命名 1.1.6 抽象类命名使用Abstract或Base开头异常类命名使用Exception结尾测试类命名以它要测试的类的名称开始以Test结尾1.1.13 对于Service和DAO类基于SOA的理念暴露出来的服务一定是接口内部的实现类用Impl的后缀与接口区别1.1.13 如果是形容能力的接口名称取对应的形容词做接口名1.1.14 枚举类名建议带上Enum后缀枚举成员名称需要全大写单词间用下划线隔开1.1.11 如果使用到了设计模式建议在类名中体现出具体模式1.1.9 包名统一使用单数形式类名如果有复数含义类名可以使用复数形式 上述规则主要在讲述具体命名一个类的时候的一个「用词规范」。这些用词规范绝大多数实际上也是一种约定俗成比如Abstract前缀Exception后缀等等。 对于接口的命名笔者最为不能忍受的一种命名就是将所有的接口以大写字母I开头诸如IUserService。真是一种坑爹到极致的命名第一IUserService纠结是一个啥玩意儿好好的UserService加上一个大写字母I直接失去了阅读时的语义性第二谁他妈知道你这东西到底是大写字母I还是数字1啊 有关枚举类名是否加上Enum后缀笔者对此有所保留。在笔者的团队中是不使用Enum作为后缀的但对此并不反感。至于枚举成员名称不用大写字母并下划线隔开的基本属于缺心眼行为。Enum的设计初衷就是对常量的规整和扩展所以命名规范继承自常量是比较合理的一种选择。 在命名中体现设计模式相信这一点很多程序员都能遵守。因为在笔者看来能在代码中熟练使用设计模式的同学通常也不会是一个对自己毫无要求的烂货。这条命名规范在Spring以及很多优秀的开源项目中都有很深刻的体现。 类名是否可以使用复数形式相信主要的分歧来自于工具类。笔者的规定是 提供一系列静态方法的工具类一概使用Utils作为后缀命名 这条规范的依据主要来自于笔者发现commons和spring这两个比较优秀的开源框架中提供的工具类通常都带s结尾。 常量规约 1.2.1 不允许出现任何魔法值即未经定义的常量直接出现在代码中1.2.3 不要使用一个常量类维护所有常量应该按常量功能进行归类分开维护1.2.4 常量的复用层次有五层跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量1.2.5 如果变量值仅在一个范围内变化用Enum类。如果还带有名称之外的延伸属性必须使用Enum类1.1.12 尽量不要在接口里定义变量如果一定要定义变量肯定是与接口方法相关并且是整个应用的基础常量 常量规约的核心有两点第一别使用常量第二让常量可控。 常量的存在按照笔者个人的理解是向下兼容的选择因为JDK1.5之后才出现枚举外加用起来足够爽想象一下静态调用时引用的便捷性甚至基本类型可以直接参与业务逻辑的计算。 所以在上述规则中我们可以看到常量进化到枚举的趋势也能看到由于用起来足够爽带来的常量管理需求要求分组1.2.3以及要求放在合适的位置1.2.4。 有关分组笔者有不同意见常量分组未必要分散到不同的类在一个常量类中定义静态类也是一种分组方式有时候这样的分组方式可能管理起来更有效。 至于接口中只能定义常量不能定义变量基本就属于幼儿园规则了。 语法糖 1.2.2 long或者Long初始赋值时必须使用大写的L不能是小写的l小写容易跟数字1混淆造成误解1.1.12 接口类中的方法和属性不要加任何修饰符号public 也不要加保持代码的简洁性并加上有效的Javadoc注释1.4.2 所有的覆写方法必须加Override注解1.4.3 可变参数必须放置在参数列表的最后。提倡同学们尽量不用可变参数编程1.4.4 对外暴露的接口签名原则上不允许修改方法签名避免对接口调用方产生影响。接口过时必须加Deprecated注解并清晰地说明采用的新接口或者新服务是什么1.4.5 不能使用过时的类或方法1.4.10 序列化类新增属性时请不要修改serialVersionUID字段避免反序列失败1.4.17 循环体内字符串的联接方式使用StringBuilder的append1.4.18 final可提高程序响应效率1.4.19 慎用Object的clone方法来拷贝对象 有关语法糖的总结其实比较牵强因为绝大多数的规则看上去都比较小儿科比如像覆写方法的Override注解Deprecated注解可变参数的问题等等基本上都在IDE层面解决了。 当一个项目在IDE中产生了一些由于使用过时方法之类的事儿导致的warning时有洁癖的程序员应该主动修复这个warning。这也是是一个程序员的基本素养问题。 最后的三条笔者认为有点鸡肋对于初级程序员大多还到不了考虑final和clone的层次而中高级程序员这几条规则对他们而言并无问题。 基本类型 1.2.2 long或者Long初始赋值时必须使用大写的L不能是小写的l小写容易跟数字1混淆造成误解1.4.7 所有的相同类型的包装类对象之间值的比较全部使用equals方法比较1.4.8 所有的POJO类属性必须使用包装数据类型1.4.8 RPC方法的返回值和参数必须使用包装数据类型1.4.8 所有的局部变量【推荐】使用基本数据类型 有关基本类型的声明1.2.2和比较1.4.7这两条规则比较直观不再叙述。 而有关基本类型和包装类型的使用这东西一直是吵架的核心。用还是不用这是个问题很显然阿里同学的观点是为了提高程序的容错性和扩展性尽可能使用包装类型。 从阿里同学举的例子来说也是有一定说服力的 数据库的查询结果可能是null因为自动拆箱用基本数据类型接收有NPE风险比如显示成交总额涨跌情况即正负x%x为基本数据类型调用的RPC服务调用不成功时返回的是默认值页面显示0%这是不合理的应该显示成中划线-。所以包装数据类型的null值能够表示额外的信息 不过笔者认为如果程序员对程序能够驾驭得比较好基本类型也是一种很好的选择。因为基本类型有一些比较好用的特性比如说默认值。笔者在这里也举个例子进行说明 通常我们都会用is_disabled字段在数据库中表示某一个表的记录是否被逻辑删除而这个字段在Java代码中被映射成什么类型呢Boolean如果被映射成包装类型那么数据库里面的这个字段就可以为null有些读者会说这并没有什么问题啊。可是数据库is_disabled字段如果为null代表什么逻辑含义呢这条记录究竟是有效还是无效如果这个字段不能为null那么将其映射成基本类型是一个皆大欢喜的事情既保证了数据库数据的完整性我们在初始化的时候还可以忽略这个字段因为boolean天生的默认值就是false 所以笔者对于包装类还是基本类型的结论是 一切跟着业务的实际情况而定基本类型也有其生存空间 方法命名 1.1.15 Service/DAO层方法命名规约- 获取单个对象的方法用get做前缀- 获取多个对象的方法用list做前缀- 获取统计值的方法用count做前缀- 插入的方法用save推荐或insert做前缀- 删除的方法用remove推荐或delete做前缀- 修改的方法用update做前缀 有关方法的命名笔者想多说几句不同意见。对于上述的规则笔者认为适合在DAO这个层次进行实践而不能应用于Service层。 使用Hibernate作为持久层框架的读者对Hibernate的API应该比较熟悉而上面的命名规范和Hibernate对外暴露的API名称是很接近的。我们知道通常到了DAO这个层次数据库操作相对来说是一个原子操作所以增删改查的语义是最适合做方法命名的。这也就是笔者认为这套规则在DAO层能够被实践得很好的一个原因。 当然上述规则中有一个例外 获取单个对象用load做前缀避免使用get 原因很简单get可能是getter方法的前缀作为一个偏执狂老子不冒风险。 话题回到Service的命名上来为什么笔者不认同使用相同的命名规范作用于Service层呢因为Service层通常是对外暴露的接口具有一定的业务意义也就是说Service层通常也不会是简单的增删改查而是若干原子操作的集合。 举两个很简单的例子发短信。发短信这个业务中可能包含了本地配置的读取、本地数据库的读写远程服务的调用。我们可以看到这是一连串数据库操作甚至是异构系统调用的集合实现能用简单的增删改查来命名吗所以笔者的观点很简单 Service层接口方法的命名应还原业务的本来面目采用动词或者动宾结构来进行方法的命名 举例来说resetPassword / login / sendMessage 都是比较合理的命名方式。 方法和属性 1.4.9 定义DO/DTO/VO等POJO类时不要设定任何属性默认值1.4.11 构造方法里面禁止加入任何业务逻辑如果有初始化逻辑请放在init方法中1.4.14 当一个类有多个构造方法或者多个同名方法这些方法应该按顺序放置在一起便于阅读1.4.15 类内方法定义顺序依次是公有方法或保护方法 私有方法 getter/setter方法1.4.16 setter方法中参数名称与类成员变量名称一致this.成员名参数名。在getter/setter方法中尽量不要增加业务逻辑1.4.20 类成员与方法访问控制从严 这几条规约理解起来不难执行起来也不难。要探究背后的原因可能就需要花点功夫。 比如构造方法和setter/getter方法禁止加入业务逻辑主要是这些方法有很大概率被程序框架的反射机制调用。一旦其中含有业务逻辑那么调试和定位就会变成灾难。 不过对于getter方法通常要网开一面。因为在实际情况中我们往往会在一个POJO中加入额外的getter方法用于序列化或者内部逻辑的使用。在这种情况下避免和其他getter方法产生分歧是需要注意的地方。 至于说到类内的方法定义顺序笔者基本同意上述规则但在实际执行中可能更加严格getter和setter方法的顺序也有严格讲究必须是先getter方法后setter方法而不是让它们成对出现。 有关类成员和方法的访问控制阿里的同学洋洋洒洒说了好几条语法层面偏多在这里就不再详细展开。 格式规约 格式规约是代码规范中争议最大的由于条目众多在这里就不逐一解读挑选几条大致说一下。 1.3.5 缩进采用4个空格禁止使用tab字符1.3 6. 单行字符数限不超过 120 个1.3.8 IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式不要使用windows格式1.3.10 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行 绝大多数情况下空格党和Tab党的较量是空格党完胜。笔者也不记得是多少年前被一位前辈教育说禁止使用Tab就保持了良好的习惯至今。对于缩进个人比较赞同4个空格但HTML等页面上使用4个空格的话一些复杂页面的缩进就会比较恐怖此时可以降级为2个空格。 对于单行字符数的限制不超过120个这条规则笔者完全不能认同。这里面牵涉到的情况比较多不能一棒子打死了。有些逻辑有大量的分支和循环的嵌套如果遵循4个空格的缩进原则碰到方法名称还比较长的状况就要折行这给代码阅读带来极大的困扰另外有一种情况就是需要额外进行比较长的注释编写不能写在一行里的感觉真是比较糟糕因为还得考虑断句才不影响阅读。另外笔者有一个习惯是在条件语句边上加一句注释这样就有很大概率会超出120字 有人会问条件语句边上加注释是什么鬼从上面的代码上可以看到条件语句上面的一行注释实际上在解释整个代码片段而条件语句边上的注释说明的是条件语句本身当然如果读者有更好的写注释的位置请及时给笔者留言。 文件的UTF-8和Unix格式没什么好说的IDE支持的也很好。但这一点对初级程序员尤为重要我已经不知道多少次就这个问题惩罚过实习生了。 有关语句组空行是笔者极力推荐的一个做法。这不仅仅是为了空行而空行这里的空行本身就是一种编程思路的整理。而笔者还有一个习惯就是对比较复杂的逻辑都在语句组的前面加上注释注释也用编号编排这样回头debug时也会极大提升效率。 集合类型 阿里规范中的集合类型这个章节感觉写得比较鸡肋。绝大多数的规范似乎都是针对初级程序员的。笔者看了半天也没总结出一条值得额外解读的所以权当复习一遍基础知识就好。 并发处理 1.6.1 获取单例对象需要保证线程安全其中的方法也要保证线程安全1.6.2 创建线程或线程池时请指定有意义的线程名称方便出错时回溯1.6.3 线程资源必须通过线程池提供不允许在应用中自行显式创建线程1.6.4 线程池不允许使用Executors去创建而是通过ThreadPoolExecutor去创建 上面这4条规则主要是针对线程的创建和使用。由于Spring的存在其实上述情况不太可能发生。 1.6.5 高并发时同步调用应该去考量锁的性能损耗。能用无锁数据结构就不要用锁能锁区块就不要锁整个方法体能用对象锁就不要用类锁1.6.7 对多个资源、数据库表、对象同时加锁时需要保持一致的加锁顺序否则可能会造成死锁1.6.8 并发修改同一记录时避免更新丢失要么在应用层加锁要么在缓存加锁要么在数据库层使用乐观锁使用version作为更新依据 上面这3条规则主要是针对锁。不过这几条规则看上去更像是3道面试题的答案。这3道面试题分别是 使用锁同步有什么需要注意的地方什么是死锁举例说明什么情况会发生死锁什么是乐观锁什么是悲观锁分别用在什么场景 相信能解答上述面试题的同学应该对上面的原则了然于心。 1.6.9 多线程并行处理定时任务时Timer运行多个TimeTask时只要其中之一没有捕获抛出的异常其它任务便会自动终止运行使用ScheduledExecutorService则没有这个问题1.6.10 使用CountDownLatch进行异步转同步操作每个线程退出前必须调用countDown方法线程执行代码注意catch异常确保countDown方法可以执行避免主线程无法执行至countDown方法直到超时才返回结果回溯1.6.13 volatile解决多线程内存不可见问题。对于一写多读是可以解决变量同步问题但是如果多写同样无法解决线程安全问题1.6.14 HashMap在容量不够进行resize时由于高并发可能出现死链1.6.15 ThreadLocal无法解决共享对象的更新问题ThreadLocal对象建议使用static修饰。这个变量是针对一个线程内所有操作共有的所以设置为静态变量所有此类实例共享此静态变量 上面这些规则基本上属于知识贴范畴可以一带而过有些可能不太会实际碰到。像定时任务可能使用Spring的封装更多一些而Spring默认就是使用ScheduledExecutorService的。 而CountDownLatch的异常捕获也是一个老生常谈的问题了属于多线程编程的基本功。 最后的三条对于写应用的同学接触不多但写底层的同学应该会很熟悉。 注释规约 注释规约的内容比较多这里也仅挑选一些具有代表性的进行解读 1.8.5 所有的枚举类型字段必须要有注释说明每个数据项的用途1.8.6 与其半吊子英文来注释不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可1.8.8 注释掉的代码尽量要配合说明而不是简单的注释掉1.8.10 好的命名、代码结构是自解释的注释力求精简准确、表达到位 枚举类加注释是非常必要的因为枚举通常是都是常量的扩展而常量是需要说明的。 鉴于很多程序员的英语水平笔者建议英语不够好的程序员直接使用中文写注释。 对于注释掉的代码笔者的意见是在绝大多数情况下应该直接删除除非在很短的时间内还有恢复的余地。 有关什么是好的命名和代码结构什么样的命名能够使得代码做到自解释笔者将另外撰文进行说明。 数据库规约 数据库规约本身并不属于Java规约的范畴不过阿里的规范中包含了不少数据库规约的内容所以笔者也同样加以解读。 3.1.1 是与否概念的字段必须使用is_xxx的方式命名数据类型是unsigned tinyint 1表示是0表示否3.1 2 表名、字段名必须使用小写字母或数字禁止出现数字开头禁止两个下划线中间只出现数字3.1.3 表名不使用复数名词3.1.4 禁用保留字3.1.5 唯一索引名为uk_字段名普通索引名则为idx_字段名3.1.10 表的命名最好是加上业务名称_表的作用3.1.11 库名与应用名称尽量一致 上述规约主要说的是库、表、字段的命名规约。应该说绝大多数的上述规约都是参考项需要根据实际情况进行调整我们逐条来说。 有关布尔值的数据库映射对于使用is_xx进行命名没有异议对于数据类型是否应该使用tinyint稍有保留笔者实际上使用bit更多。由于布尔值所对应的Java类型是boolean所以笔者通常在命名时利用boolean的默认值特性对一些常用的命名进行更加严格的规定。比如「是否有效」命名成为「is_disabled」就要比「is_enabled」来的好。因为 is_disable false 是绝大多数程序的事实逻辑这样就可以利用boolean值默认为false的特性。 Java中的绝大多数命名都使用驼峰法而数据库的命名实际上更加严格。光光小写是不够的而是要强制使用下划线命名法主要是因为SQL是大小写不敏感的语言。笔者在实际工作中经常看到使用驼峰法命名表名或者字段名的这种基本上属于小学没毕业的行为。 有关表名不能使用复数不能使用关键字这些属于比较基础的命名规范应该遵守。但是笔者在这里提出更为严格的要求不仅不能使用SQL关键字进行命名同样不允许使用Java关键字因为绝大多数情况数据库字段会被映射到相应的Java对象如果可以使用Java关键字那么映射的时候就是自找麻烦了。 最后三条规约属于建议相信每个公司都有自己独特的规定。比如笔者见过有一些写Oracle出身的程序员习惯使用tbl_做表名的前缀使用vw_做视图的前缀。个人觉得这个方面不宜做过多规定只要团队保持风格整体一致即可。 3.1.6 小数类型为decimal禁止使用float和double3.1.7 如果存储的字符串长度几乎相等使用char定长字符串类型3.1.8 varchar是可变长字符串不预先分配存储空间长度不要超过5000 这三条主要说的是数据库设计时的类型规约。 除了上述三条之外在笔者团队另外还会遵守如下几条 明确日期和时间日期使用date类型并使用xxDate进行Java字段命名时间使用date_time类型并使用xxTime进行Java字段命名以示区分 上面这条主要是和日期时间有关的强制这样的规约对于提升代码的可读性是有帮助的。 枚举类型在数据库中既可以映射成int也可以映射成varchar视实际情况定 通常对于排序和检索有强依赖的枚举类型映射成int比较理想否则可以映射成varchar。虽然从效率上说int基本上会强于varchar但varchar毕竟可读性更好所以还是应该一分为二来看。 3.1.9 表必备三字段id, gmt_create, gmt_modified3.1.8 如果存储长度大于此值定义字段类型为text独立出来一张表用主键来对应避免影响其它字段索引效率3.1.12 如果修改字段含义或对字段表示的状态追加时需要及时更新字段注释3.1.13 字段允许适当冗余以提高性能但是必须考虑数据同步的情况3.1.14 单表行数超过500万行或者单表容量超过2GB才推荐进行分库分表3.1.15 合适的字符存储长度不但节约数据库表空间、节约索引存储更重要的是提升检索速度 上面的规约主要涉及到一些数据库表设计上的原则。 其中3.1.8是非常值得大家注意的一点笔者个人的习惯是对于大字段拆表的同时优化SQL尽可能做到用主键单独取大字段避免产生效率瓶颈。 而3.1.14是希望提醒一些自视甚高的架构师不要过早的进行过度设计。这里笔者提一点 对于每一张数据库表的设计应该预估表在未来若干时间段内的数量以采取最佳的程序处理措施 这里所说的最佳程序处理措施包括并不限于使用应用级别缓存对数据库进行减压选取合适的时间点对表进行分库分表是否进行人为拆表以保证较快的SQL执行等等。 有关3.1.13我们在有关SQL编写环节还会说到。 3.2.1 业务上具有唯一特性的字段即使是组合字段也必须建成唯一索引3.2.2 超过三个表禁止join3.2.3 在varchar字段上建立索引时必须指定索引长度3.2.4 页面搜索严禁左模糊或者全模糊3.2.5 如果有order by的场景请注意利用索引的有序性3.2.7 利用延迟关联或者子查询优化超多分页场景3.2.9 建组合索引的时候区分度最高的在最左边 上述规约主要讲的是和索引相关的内容。对于这块笔者不是专业的DBA所以只是挑了其中和程序开发特别有关的来讲一讲。 比如3.2.2的禁止超过3个表的join在笔者的团队中规定更为严格 禁止超过2个表的join语句出现在程序中 其实不许使用join是很多初级程序员非常不能理解的。要说明白这个问题估计又要长篇大论笔者会另辟文章进行说明。但这里还是引用一下robbin的观点笔者表示深刻赞同 另外有关严禁使用全模糊查找建组合索引时区分度最高的往左放这些原则在一定程度上会改变我们编写程序的习惯所以应该时刻注意。 3.3.1 不要使用count(列名)或count(常量)来替代count(*)3.3.5 在代码中写分页查询逻辑时若count为0应直接返回避免执行后面的分页语句3.3.6 不得使用外键与级联一切外键概念必须在应用层解决3.3.7 禁止使用存储过程存储过程难以调试和扩展更没有移植性3.3.9 in操作能避免则避免若实在避免不了需要仔细评估in后边的集合元素数量控制在1000个之内3.4.1 在表查询中一律不要使用 * 作为查询的字段列表需要哪些字段必须明确写明 上面的这几条属于SQL编写规约。阿里的规范中洋洋洒洒讲了很多条实际上都是在给程序员提个醒笔者在这里不在赘述 有关count(*)的争论一直有大量的说法。此次阿里的规范总算为count(*)党找到了SQL标准应该说也基本为这件事情画上了句号。 有关外键和级联笔者稍有困惑的是外键。因为按照笔者的理解外键影响数据库插入的速度应该有限与外键约束带来的好处相比或许还是有外键更好一些有这方面经验的读者可以留言指点迷津。级联是恶魔必须禁止。 至于存储过程或许Oracle出身的DBA会跳出来唱反调了。笔者的观点和阿里相同存储过程很难移植和维护应该抛弃。 有关表查询中不许使用 * 作为查询的字段列表这点或许能够成为规约但笔者并不十分认同。尤其是对于使用Hibernate作为ORM工具的同学来说这条规则执行起来有难度。 代码风格 1.7.1 在一个switch块内每个case要么通过break/return等来终止要么注释说明程序将继续执行到哪一个case为止在一个switch块内都必须包含一个default语句并且放在最后即使它什么代码也没有 这条主要是期待程序员人为把握好代码的执行逻辑。对于switch语句如果没有终止语句会依次执行每一个case块。实际上笔者认为switch语句是一个比较差的语法糖通常情况下都可以用更加优雅的方式来写包括并不限于使用设计模式。所以在笔者的团队中是禁止使用switch语句的。 1.7 2 在if/else/for/while/do语句中必须使用大括号即使只有一行代码避免使用下面的形式if (condition) statements; 这一条比较有意思因为这种一行式的代码风格在javascript里面经常会看到所以很多全栈工程师也会把它引入到Java中来。笔者对此并不反感但确实在可读性上不那么友好。 1.7.3 推荐尽量少用else if-else的方式可以改写成
if(condition){
...
return obj;
}
// 接着写else的业务逻辑代码;1.7.3 如果非得使用if()...else if()...else...方式表达逻辑【强制】请勿超过3层超过请使用状态设计模式 上面这点笔者比较认同因为else不仅会带来大段的代码缩进的困扰同时也会降低代码的可读性。不过对于那些坚持必须在代码的最后一行统一return的同学上面的写法可能就不太容易接受了。实际上上述代码结构比较常见于Spring的源码中倒不是尽早return而是else的逻辑块可能直接throw异常出去了。 1.7.4 除常用方法如getXxx/isXxx等外不要在条件判断中执行其它复杂的语句将复杂逻辑判断的结果赋值给一个有意义的布尔变量名以提高可读性
boolean existed (file.open(fileName, w) ! null) (...) || (...);
if (existed) {
...
} 有关这一条补充说明一下将复杂的逻辑判断结果赋值给一个有意义的布尔变量名除了提高可读性之外实际上能够极大方便调试。但笔者认为单单只是抽取部分代码并不能提高可读性而是应该将复杂的逻辑判断进一步封装为一个方法 上面的代码片段中左边是阿里风格右边是陆老师的风格大家可以比较一下哪个更好哪个更符合面向对象的思维呢 1.7.5 循环体中的语句要考量性能以下操作尽量移至循环体外处理如定义对象、变量、获取数据库连接进行不必要的try-catch操作 这一条值得说一下因为有些代码会走得比较深写着写着就忘了它处于循环体的内部了。所以保持一个谨慎的心态比较重要。 1.7.7 方法中需要进行参数校验的场景
1 调用频次低的方法。
2 执行时间开销很大的方法参数校验时间几乎可以忽略不计但如果因为参数错误导致中间执行回退或者错误那得不偿失。
3 需要极高稳定性和可用性的方法。
4 对外提供的开放接口不管是RPC/API/HTTP接口。
5 敏感权限入口。1.7.8 方法中不需要参数校验的场景
1 极有可能被循环调用的方法不建议对参数进行校验。但在方法说明里必须注明外部参数检查
2 底层的方法调用频度都比较高一般不校验。毕竟是像纯净水过滤的最后一道参数错误不太可能到底层才会暴露问题。一般DAO层与Service层都在同一个应用中部署在同一台服务器中所以DAO的参数校验可以省略
3 被声明成private只会被自己代码所调用的方法如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题此时可以不校验参数 这两条说的是参数校验说的比较在理也比较全面。比起很多公司的奇葩规定来说要人性化得多。笔者认为需要补充的是参数的校验主要还需要从格式和业务两个层面进行考量。业务层面的校验往往要比单纯的格式校验更为复杂所以在写代码时可以建立一定层次的假设当然这可能也会引入团队沟通的问题需要根据实际情况权衡。 有关阿里代码风格方面的解读受限于阿里自身提出的规约比较少。对此笔者是稍有失望的。因为代码风格规约是最能够体现一个团队对于代码整洁程度的一个衡量标准。所以笔者忍不住在这里多加了几条笔者团队的共识供读者参考 在任何情况下代码量越少越容易维护 基于上面的原则笔者的团队会鼓励使用三目表达式对简要的if/else进行重构 当然像下列左侧的代码也会重构成右侧的 一个复杂的Service层逻辑不应超过30行否则需要进行逻辑规整和抽象 在业务逻辑中尽可能不要使用setter方法而是使用构造函数或者封装成一个有逻辑意义的方法提高代码的可读性 什么连setter方法都不让用这是什么SB一样的规约啊事实上笔者团队确实是这么做的我们来看一下代码 在上面的代码中左侧代码中的setter方法调用会被封装到ShuttleOrder对象中的cancel方法中去。在实际的service代码中只会出现下半部分的一行代码。 这样做的好处在于cancel这个方法被封装后shuttleOrder.cancel()的调用从可读性上要明显优于使用2句setter方法同时也为将来的逻辑扩展预留了位置。这也是面向对象的一种实践。 转载自https://www.cnblogs.com/winner-0715/p/7594254.html转载于:https://www.cnblogs.com/renyuanwei/p/9169452.html