向往优雅的代码
什么是整洁的代码:优雅的代码,每个方法只做一件事,每个类都有明确的职责,清晰的抽象层级,符合高内聚低耦合的特性。
高内聚:组件本身应该尽可能的包含其所实现功能的所有重要信息和细节,以便让维护者无需跳转到其它多个地方去了解必要的知识。
低耦合:组件之间的互相依赖和了解尽可能少,以便在一个组件需要改动时其它组件不受影响。
想一次性写出满意的整洁代码是不可能的事。平庸的程序员只满足于写出可工作的代码,然后马上又投入到下一份可工作的代码中,而不会去调整和优化自己输出的代码。
今天,整理一下《代码整洁之道》里的代码规范指导。
命名的规范:
解析意义更好的长命名比为了省字数而不能诠释意图的短名称要好。
起名要尽量诠释代码,避免误导和不详尽。
作用域更大的变量,名称应该更长。例如作用域只在三五行的作用域内,使用 int i 这种是没有问题的。
同一作用域内避免出现拼写相近的命名,会有很严重的误导作用,影响阅读和维护性。
前缀其实大可不必。影响代码的可搜索性。
函数的规范:
函数应该短小再短小,每个函数应该只做一件事。
每个函数中的语句应该在同一个抽象层级上,该层级应该是函数名所示操作的下一层操作。
函数使用描述性的语言。长而具有描述性的名称,比短而令人费解的名称要好。
函数参数应该尽量少。唯一的例外是要强调函数调用的时序耦合时,上一个函数的返回参数可以作为下一个调用方法的入参,虽然增加了参数,可是好处是阅读时能一眼看到方法的时序,并避免调用顺序出错。
函数的入参尽量避免 boolean 值,宁愿加多和方法也比这种标识参数要好,标识参数同时也表示着该函数不止做一件事。
使用异常处理代替返回码,抽离 try/catch 代码块,异常处理就是一件事。
封装重复的代码。
代码的格式:
代码概念之间空行来分割。
方法内的变量定义应该尽量靠近他被使用的地方。
类的变量定义应该在类的头部位置。
相关的函数应该互相靠近,避免跳跃性阅读,而且要遵循自上向下的原则。
横向的格式上。空格用来分割参数,空格同时也可以用来分割代码执行的逻辑,例如表现乘法和加法的优先级。
应该遵守团队规则。
对象和数据结构:
数据结构:就是只有变量定义或者普通的 get/set 方法,例如 pojo 对象、been 对象。
对象:拥有行为的对象,隐藏了数据的实现。
对象便于添加新的数据类型(多态)。而 数据结构+过程式代码 便于为已有的数据类型添加新的行为方法。
模块不应该了解它所操作对象的内部情形,也就是说方法不应调用由其他函数返回的对象的方法。
错误处理:
使用异常而非返回码(当然,rpc 场景还是无可避免返回码)
异常应传递足够的环境信息说明。
根据使用者需要定义异常类。
定义常规流程,catch 块中避免业务逻辑处理。
别返回和传递 null 值。
边界:
应该避免我们的代码过多地了解第三方代码中的特定信息。依靠你能控制的东西,好过依靠你控制不了的东西,免得日后受他控制。说白点,就是对第三方库不能过于依赖其实现里的特定逻辑。
我们可以封装对第三方 api 的调用,改用我们熟悉的业务异常类。还有一个好处是可以隐藏内部数据结构,以后扩展和修改会容易一些。当然,这点也是看实际场景,不是说每一步都需要封装,过于僵化。
类:
类应该短小,类应该更加短小。
类应该从一组变量列表开始,如果有公共静态变量,应该先出现。然后是私有静态变量,以及私有实际变量,很少会有公共实际变量。
公共函数应该跟在变量列表之后。由某个公共函数调用的私有工具函数应该紧随在该公共函数后面。
类应该符合单一权责原则,类只有一条加以修改的理由。
类应该保持内聚,内聚的意思是类中的每个方法操作的实例变量越多,则越内聚。如果类中的每个变量都被每个方法所使用,则该类有最大的内聚性。
类应该依赖于抽象而不是依赖于具体实现细节。因为需求改变时,细节改变时,则会带来风险(修改和重新编译构建)。
系统设计:
将系统的构造和使用分开,每个程序都应该留意启动过程。
工厂和依赖注入有利于构造和使用分开。
关注点分离的系统有利于扩容。例子是 aop 切面编程。
设计系统或者模块,应该使用大概可工作的最简单方案。
迭代改进:
运行所有测试,越可测试的系统则也是越松耦合和整洁的。
不可重复,重复代表着额外的工作和不必要的复杂度。
表达了程序员的意图,写完可工作的代码之后,要进一步调整和优化,尽力用代码清晰表达意图。
尽可能减少类和方法的数量。
评论