一、课程总结
13类典型坏味道
1. 命名问题:缺乏业务含义
- 命名不应过于宽泛(如
processChapter),而应描述意图而非实现细节 (如startTranslation)。 - 避免使用技术术语命名(如
bookList),应使用业务语言 (如books)。 - 方法名应为动词或动宾结构(如
completeTranslation而非completedTranslate)。
2. 重复代码
- 复制粘贴是技术债的温床。应遵循 DRY(Don't Repeat Yourself)原则。
- 即使是结构相似的异常处理、日志上报等,也应提取为公共逻辑。
3. 长函数与大类
- 函数应尽量短小(建议不超过20--40行),一个函数只做一件事。
- 类过大往往违反单一职责原则,应通过拆分职责、分组字段等方式"瘦身"。
4. 长参数列表
- 参数过多会降低可读性,推荐封装为参数对象(如使用 Builder 模式)。
- 区分变动频率不同的参数,静态参数可内化,动态参数才需传入。
5. 滥用控制结构(如 if/else)
- 避免深层嵌套,提倡"早退"(early return)策略。
- 对于复杂分支,可考虑多态、策略模式或状态模式替代条件判断。
6. 缺乏封装
- "火车残骸"式调用(如
book.getAuthor().getName())违反迪米特法则。 - 应通过隐藏委托关系 (如在 Book 中提供
getAuthorName())提升封装性。
7. 可变数据与 setter 滥用
- 暴露 setter 破坏封装,导致状态不可控。
- 推荐用行为方法 替代(如
book.approve()而非book.setReviewStatus(...))。
8. 变量声明与赋值分离
- 变量应一次性初始化完成,避免先声明为 null 再赋值。
- 可通过提取辅助函数(如
toEpubStatus(response))保持主逻辑清晰。
9. 不一致的代码风格
- 同一系统中应保持命名、结构、抽象层次的一致性。
- 例如:函数中混合业务动作与底层细节(如 HTTP 构造)属于"层次混乱",应提取子函数统一抽象层级。
10. 落后的代码风格
- 积极采用现代语言特性(如 Java 8+ 的 Optional、Stream、Lambda)。
- Lambda 表达式应简洁,复杂逻辑需提取为命名函数。
11. 依赖混乱
- 业务代码不应直接依赖外部接口的具体实现(如 HttpClient、Redis)。
- 应引入防腐层(Anti-Corruption Layer) 隔离外部变化。
12. 代码评审与持续改进
- 频繁、小粒度的代码评审能及早暴露设计与规范问题。
- 鼓励结对编程作为极致的实时反馈机制。
13. 应对新需求的破坏性
- 新功能应尽量扩展而非修改现有核心类和接口。
- 谨慎对待实体和 API 的变更,它们是系统稳定性的关键。
二、结课测试
1.下面关于控制语句的讨论中,正确的是:
plain
A.if、for 这些控制语句都是源自结构化编程的产物,如今它们在一些场景下,有了更好的替代方案
B.在大多数情况下,else 语句是可以被优化掉的
C.switch 语句本身不是问题,但重复的 switch 往往意味着缺乏一个模型
D.多层的缩进会增加代码的理解难度
答案:ABCD
2.下面的函数 / 变量 / 类命名中,哪些一眼就能看出来是有问题的?
plain
A void processChapter(long chapterId) {
...
}
B List bookList = service.getBooks();
C void approveChapter(long chapterId) {
...
}
D class ChapterServiceImpl {
...
}
答案:ABD
3.下面关于 getter/setter 讨论中,不正确的是:
plain
A.写代码的时候,写完字段之后,应该立刻生成 getter/setter
B.getter/setter 本质上等于暴露了类实现的细节,破坏了封装
C.setter 提供修改数据的接口,应该尽可能规避
D.即便需要修改数据,setter 也不是一个好的命名,应该用更有业务含义的命名
答案:A
4.如果新需求到来,我们应该怎么做?
plain
A.新增业务,就要新增接口
B.在一堆判断条件后面,增加一个判断条件就好了
C.需要内部变化的,直接去修改实体
D.从业务层面谨慎地评估要做的修改
答案:D
5.下面关于重复代码的讨论中,正确的是:
plain
A.只有复制粘贴才会产生重复的代码
B.重复代码违反了 DRY 原则
C.不仅名词的重复是重复,动词的重复也是重复
D.if..else 代码块中语句高度类似也是一种重复
答案:BCD
6.下面关于重构的讨论中,正确的是:
plain
A.重构是一种模式匹配,识别坏味道,采用对应的重构手法
B.重构就是重写一段代码
C.保证重构正确性最好的方式是测试
D.重构是小步地调整
答案:ACD
7.下面关于 static 函数讨论中,正确的是:
plain
A.static 方法用起来很方便,可以多使用
B.static 函数自身不容易测试,所以,应该少用
C.static 函数 / 变量本质上也是一种全局函数 / 变量,所以应该少用
D.绝对不能使用 static 函数
答案:C
8.下面关于大类的讨论中,不正确的是:
plain
A.函数长点,可以看到一个业务的全貌,这是好事
B.函数小,数量就多,会影响整体的性能,所以,不能把函数写得太小
C.100 行以上的才算长函数
D.可以通过提取方法消除长函数
答案:ABCD
题目解析
选项 A,大类难以理解,本身就是坏味道
选项 B,类多了,可以采用包或命名空间进行封装
选项 C,实体类到表的映射有很多框架可以做,多个类也可以映射到一张表上
选项 D,类的字段要根据职责来
9.下面关于火车残骸代码的讨论中,正确的是:
plain
A.像 book.getAuthor().getName() 这种连续多个函数的调用,属于火车残骸代码
B.火车残骸代码是违反迪米特法则的
C.所有连续多个函数调用的,都属于火车残骸
D.通过隐藏委托关系消除火车残骸的代码
答案:ABD
10.下面关于长函数的讨论中,正确的是:
plain
A.函数长点,可以看到一个业务的全貌,这是好事
B.函数小,数量就多,会影响整体的性能,所以,不能把函数写得太小
C.100 行以上的才算长函数
D.可以通过提取方法消除长函数
答案:D