编程素养提升之基础技艺

前言

如何才算是一个伟大的程序员,真的不是卷了多久的算法,刷了多少leecode,当然这在面试的时候很受用。真正伟大的程序员是他写的代码不会被后来者破口大骂,是可以给公司给同事一个整洁优雅可维护的代码库。所以回归本质、回归基础,让我们用工匠精神写出经得起推敲的简单代码吧。
阅读《代码精进之路》笔记


一、命名

起一个好名字应该很难,因为一个好名字需要把要义浓缩在一到两个词中

命名的过程本身就是一个抽象和思考的过程,在工作中,当我们不能给一个模块、一个对象、一个函数,甚至一个变量找到合适的名称的时候,往往说明我们对问题的理解还不够透彻,需要重新去挖掘问题的本质,对问题域进行重新分析和抽象,有时还要调整设计和重构代码。因此,好的命名是我们写出好代码的基础。
通常,如果你无法想出一个合适的名字,很可能意味着代码“坏味道”、设计有问题。这时可以思考一下:是不是一个方法里实现了太多的功能?或者类的封装内聚性不够?又或者是你对问题的理解还不够透彻,需要获取更多的信息?

1.有意义的命名

变量名: 应该是可正确描述业务,有表达力的,不需要额外注释补充说明的名词。

函数名: 避免空泛的命名,提升抽象层次,体现业务语义;例:假如我们将雇员信息存储在一个栈中,现在要从栈中获取最近存储的一个雇员信息,那么getLatestEmployee()就比popRecord()要好。

类名: 实体类承载了核心业务数据和核心业务逻辑,其命名要充分体现业务语义,并在团队内达成共识;辅助类是辅佐实体类一起完成业务逻辑的,其命名要能够通过后缀来体现功能。

包名: 包(Package)代表了一组有关系的类的集合,起到分类组合和命名空间的作用。包的命名要适中,不能太抽象,也不能太具体。

模块名: 名称要反映模块在系统中的职责,代表着架构层次。

2.保持一致性

每个概念一个词:每个概念对应一个词,并且一以贯之。如下,统一方法名前缀:

新增 create
添加 add
删除 remove
修改 update
查询(单个结果) get
查询(多个结果) list
分页查询 page
统计 count

使用对仗词:有助于保持一致性,且易于理解。如下:

add/remove
increment/decrement
open/close
begin/end
insert/delete
show/hide
create/destroy
lock/unlock
source/target
first/last
min/max
start/stop
get/set
next/previous
up/down
old/new

后置限定词:限定词加到名字的最后,并在项目中贯彻执行,保持命名风格的一致性。变量名中主要含义的部分应位于最前面,这样可以突出显示。

常见限定词:TotalMaxMinAverage

3.自明的代码

统一业务语言:确保团队在内部的所有交流、模型、代码和文档中都要使用同一种编程语言。

统一技术语言:有些技术语言是通用的,业内人士都能理解,我们应该尽量使用这些术语来进行命名。这些通用技术语言包括DO、DAO、DTO、ServiceI、ServiceImpl、Component和Repository等。

小心注释:注释的作用是阐述代码背后的意图,而不是复述代码功能,复述功能意味着坏味道

二、规范

在工作中,很多工程师向我抱怨他们的系统很凌乱,毫无章法可言,即使花费很长时间也很难理清系统的脉络。在评估一个需求时,要在杂乱无章的代码中找好久才能找到相关的需求改动点,然而真正需要改动的代码可能只有一行而已。这样的无序在很大程度上是系统缺少代码组织结构规范造成的。

1.代码规范

1、代码格式:代码格式关系到代码的可读性,因此需要遵从一定的规范,包括缩进、水平对齐、注释格式等。关于代码格式,可能会因为语言和个人偏好而不同,但是一个团队最好是选定一种格式,因为一致性可以减少复杂度。代码格式的规范不是绝对的,没有一种比另一种更好的说法。它其实是一种约定,一旦约定下来,固化成IDEA/Eclipse IDE代码的统一模板。

2、空行规范:空行是“无”的价值,用于概念区隔。将概念相关的代码放在一起,不同逻辑块之间,用空行分隔。相关性越强,彼此之间的距离应该越短。

3、命名规范:

  • 包名:使用小写,“.”分隔符之间有且仅有一个自然语义单词,统一使用单数形式
  • 类名:“大驼峰”形式。如Object
  • 枚举类:以Enum或Type结尾,成员名称需要全部大写,单词间下划线连接
  • 抽象类名:以Abstract开头
  • 异常类名:以Exception结尾
  • 实现类名:以Impl结尾
  • 测试类名:以被测试类名为始,以Test结尾
  • 方法名:“小驼峰”形式,一般为动词,与参数组成动宾结构。如Thread.sleep(long millis)
  • 常量名:字母全部大写,单词间用下划线分隔

4、日志规范:日志是线上故障分析、追溯的重要依靠,所以要保证日志输出格式统一,保证日志的质量

  • ERROR级别:表示不能自己恢复的错误,需要被立即关注和解决。打印异常堆栈,最好要打印上下文信息(关键数据),便于排查问题。做好ERROR输出的场景定义和规范,再配合监控治理,双管齐下,确保线上系统的稳定。
  • WARN级别:可预知的业务问题,如参数校验不通过、无访问权限等业务异常。短时间内产生过多WARN日志也是需要被关注的,为WARN配置适当阀门报警是必要的。
  • INFO级别:INFO用于记录系统的基本运行过程和运行状态。主要包括系统状态变化日志、业务流程的核心处理、关键动作和业务流程的状态变化。切忌把INFO当成DEBUG使用。
  • DEBUG级别:输出调试信息,生产环境要关闭,有问题时动态的开启DEBUG,使用配置工具根据requestId进行判断,只打印需要的日志。

5、异常规范:针对业务异常和系统异常要做统一的异常处理,类似于AOP,在应用处理请求的切面上进行异常处理收敛。

定义两个Unchecked Exception: BizException(业务异常)SysException(系统异常)
try {
    // 业务处理
} catch (BizException e) {
    // 业务异常使用warn级别
    log.warn("BizException ", e);
} catch (SysException e) {
    // 系统异常使用error级别
    log.error("SysException ", e);
} catch (Exception e) {
    // 兜底
    log.error("SysException ", e);
}

2.防止破窗

在软件工程中,“破窗效应”可谓是屡见不鲜。面对一个混乱的系统和一段杂乱无章的代码,后来人往往会加入更多的垃圾代码。这也凸显了规范和重构的价值。首先,我们要有一套规范,并尽量遵守规范,不要做“打破第一扇窗”的人;其次,发现有“破窗”,要及时地修复,不要让事情进一步恶化。

三、设计原则

遵从原则,可以事半功倍;反之,则可能带来麻烦。当然也不是要教条地遵守每一条原则,而是要根据具体情况进行权衡和取舍。SOLID原则之间并不是相互独立地,而是相互关联的。开闭原则 和 里氏代换原则 是 设计目标;单一职责原则、接口分离原则、依赖倒置原则 是 设计方法。

SRP(单一职责原则)

  • 任何一个软件模块中,应该有且只有一个被修改的原因。
  • 衡量职责单一的标准就是,“模块是否只有一个被修改的原因”。职责越单一,被修改的原因就越少,模块的内聚性就越高,被复用的可能性就越大,也更容易被理解。
  • 把重要的业务逻辑与数据放在一起,然后调用其他没那么重要的函数

OCP(开闭原则)
软件实体应该对扩展开放,对修改关闭。但是要想做到绝对的“不修改”是比较理想主义的,所以要在设计时做一定的权衡,不要提前做过多的“大设计”。

相关设计模式:装饰者模式、策略模式、适配器模式、观察者模式

LSP(里氏替换原则)
程序中的父类型都应该可以正确地被子类型替换。

  • 警惕 instanceof :如果出现强制类型转换才能使用子类函数的情况,就要考虑“提升抽象层次”来解决这个问题。即 将子类中特有函数用一种更抽象、通用的方式在父类中进行声明。

  • 子类覆盖父类函数 :子类方法覆盖了父类方法,并改变了其含义。这样做里氏替换时,就会出现问题。比如“正方形-矩形”问题

ISP(接口隔离原则)

  • 多个特定客户端接口要好于一个宽泛用途的接口。
  • 在做接口拆分时,也要满足单一职责原则。 拆分之后,只需要依赖需要的东西,这样可以降低模块之间的耦合。

DIP(依赖倒置原则)

  • 模块之间交互应该依赖抽象,而非实现。
  • “面向接口编程”也是实现DIP的一个技法
  • 所谓依赖倒置,就是要反转依赖的方法,让原来紧耦合的依赖关系得以解耦。

DRY(避免重复代码)

  • 如果多次遇到相同的问题,就应该抽象出一个共同的解决方法,不要重复开发同样的功能
  • 避免“散弹式修改”,因为是典型的代码“坏味道”,因为一个小小的改动,会牵扯很多地方。

YAGNI(你不会需要它)
是指自以为有用的功能,实际上都是用不到的。因此,除了核心的功能之外,其他功能一概不要提前设计,这样可以加大开发进程。其实就是要避免过度设计。

Rule of Three(三次原则)
是指当某个功能第三次出现时,就有必要进行“抽象化”了。

  • 第一次,写一个特定的解决方法
  • 第二次,copy代码
  • 第三次,着手“抽象化”,写通用的解决方法

要衡量 代码冗余 和 开发成本 的 平衡点。软件设计本身就是一个平衡的艺术,我们既反对过度设计,也绝对不赞成无设计。

KISS原则
目标不是越复杂越好,反而是越简洁越好

POLA原则(最小惊奇原则)
代码要简单易懂,“惊奇度”越高,复杂性越大。我确实见过很牛的架构师写的自成体系的牛掰代码,但是大部分人反馈阅读不懂,所以某种意义上讲这也不是一个好代码。


… …

张建飞.代码精进之路.2020年1月

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值