读程序员的制胜技笔记03_有用的反模式(上)

发布时间 2023-11-04 07:01:14作者: 躺柒

1. 教条

1.1. 一成不变的法则

1.2. 这些东西会遮蔽我们的双眼,你坚持相信的时间越久,你被遮蔽双眼的程度也就越深

2. 质疑所有教给你的东西

2.1. 它们有多么有用

2.2. 使用它们的理由

2.3. 使用它们的好处

2.4. 使用它们的代价

3. 反模式

3.1. 不良实践

3.2. 如果你愿意使用它们,受到一些难听的批评也是理所应当的,但这并不意味着我们应该谈虎色变

4. 若无损坏,亦可破坏

4.1. 不惜任何代价避免代码重构

4.1.1. 你所做的每一个更改都有可能带来代码从头来过的风险,因为对于一个本身运行正常的业务,对它进行更改,这个行为本身就是错误的

4.1.2. 缺陷本身代价不菲,而修改已成为新特性一部分的缺陷则更费时间

4.1.3. 从头再来比发布带有缺陷的新特性更糟糕

4.2. 坚持让原有代码不受后续影响,很可能会让你出于妥协,反而写出更多代码,增加你的代码维护量

4.3. 如果代码让你不敢破坏,那就是设计糟糕的代码

4.4. 这不是意味着好的代码不会被破坏,而是当它被破坏时,将碎片重新黏合起来要容易得多

4.5. 代码刚性

4.5.1. code rigidity

4.5.2. 现有代码对于后续的代码更改的这种阻力称为代码刚性(可以简单把它理解成耦合性)

4.5.3. 代码刚性越强,你不得不更改的代码数量也就越多,甚至牵一发而动全身

4.5.4. 多个因素影响的,其中之一就是代码中的依赖项太多

4.5.4.1. 你在物理数据库结构上增加了一个依赖项

4.5.4.2. 如果你需要改变信息表的布局或所使用的数据库技术,你就必须检查所有的代码,确保所有的东西都能与新的表布局或新的数据库技术一起工作

4.5.5. 服务多个细分客户比只为单一类型的客户服务需要更大的责任

4.5.6. 理想情况下,应该尽量减少服务客户的类型

4.5.6.1. 这是保持组件或整个层尽可能简单的关键

4.6. 代码复用

4.6.1. code reuse

4.7. 快刀斩乱麻

4.7.1. 对于代码来说,越早破坏越易修复,所以你需要识别问题,破坏代码,即使在它工作时也是如此

4.7.2. 零依赖项的组件最容易更改

4.8. 敬畏边界

4.8.1. 避免突破抽象边界

4.8.2. 抽象边界是围绕代码层次来划定的逻辑边界,表示某一层面上关注的所有问题

4.8.3. 为什么跨层是个坏主意?因为它让抽象失去了意义

4.8.3.1. 每个成员只对自己的层负责,突然一下子,Web层的开发人员需要学习SQL

4.9. 测试很重要

4.9.1. 提前对代码进行测试是更简单的方法

4.9.2. 自动化测试通常更好,因为你只需要编写一次测试,并且不会浪费时间靠自己进行测试

4.10. 隔离相同功能

4.10.1. 分离业务逻辑是有意义的

4.10.2. 业务逻辑中的业务或业务层并不一定意味着是与业务相关的东西

4.10.3. 业务逻辑更像是具有抽象模型的应用程序的核心逻辑

4.11. 不要留下技术债

4.11.1. 技术债(technical debt)

4.11.1.1. 当我们只是为了在截止日期前完成该阶段的任务而做出某些不合适的决定时,因为完成得太过匆忙,我们就更难完成下个阶段截止日期前的任务

4.11.1.2. 称为债,是因为要么你以后偿还,要么代码会在某个你意想不到的时间来找你,给你重重一击

4.11.1.3. 你的投入产出比将随着时间的推移而逐渐下降

4.11.1.4. 你的工作速度会变得越来越慢

4.11.1.5. 你从你的工作中得到的满足感会越来越少

4.11.1.6. 从管理层得到的积极反馈也会越来越少

4.11.1.7. 技术债是有意识的决定

4.11.2. 无意识造成的则被称为技术无能(technical ineptitude)

4.11.3. 做正确的懒人:为你未来的懒惰服务

4.11.4. 处理技术债的最好方法是先把它放着

5. 从头开始写

5.1. 如果说修改代码有风险,那从头开始重写代码的风险更是要比这高上几个数量级

5.2. 不仅意味着代码得从头开始写,还意味着从头开始修复所有的缺陷

5.3. 从头开始写,被认为是解决设计缺陷的一种严重低效的方法

5.4. 当你从头开始时,你会比以前更早地知道你是否走错了路

5.5. 不要犹豫,把你原来的代码扔掉,从头开始写。不要被沉没成本的谬论所影响

6. 修复它,即使它没有坏掉

6.1. 让代码不断变动,这样它就不会产生刚性

6.2. 好的代码应该是容易改变的,修改它时,不应该给你带来大量需要改变的地方

6.3. 流行的Elasticsearch搜索库的版本升级需要逐个版本进行,它不支持从一个版本直接升级到另一个版本

6.4. .NET支持绑定重定向(binding redirect),这在一定程度上避免了同一软件包有多个版本的问题

6.5. 定期更新你的包的好处

6.5.1. 你会把以往将包升级到最新版本的精力分散到维护期内

6.5.1.1. 每一步都没那么痛苦

6.5.2. 每一次小的升级都可能会以微妙的方式影响你的代码或你的设计,你需要修复这些问题才能使工作继续下去

6.6. Stack Overflow只充满了关于较新版本的答案,因为人们开启新的项目时大多会使用最新版本的包

6.7. 旧版本的支持资源会随着时间的推移而消退,这使得每过一年就更难升级,这就把你推入一个“绝望漩涡”

6.8. 保持最新的状态,让升级软件包成为一个固定的习惯

6.9. 你的应用程序对变化的阻力越小,它在设计和维护方面就越好

6.9.1. 投资你的应用程序的依赖关系的灵活性

6.10. 整洁仅次于功能

6.11. 你的代码会改变,需求会改变,文档会改变,环境也会改变,你不可能通过不碰代码来保持其稳定运行

6.12. 你不一定是在增加功能或修复错误,但当你完成后,代码应该会有轻微的改进

6.13. 要想在写代码方面变得成熟,唯一的办法就是修改大量的代码

7. 重复你自己

7.1. 这条禁令的理论基础是:你写了一段代码,在程序代码的某处恰好可以原样地用上这段代码

7.2. 将代码放在共享类或模块中,并在代码中需要用的两个部分中进行复用

7.3. 当你试图将某段代码重构为可复用类时,这个行为本身就创建了新依赖,这个新依赖会在无形中影响你的代码设计,甚至有时能成为你编写代码的掣肘

7.4. 并非所有使用这段共享代码的软件的需求都完全一致

7.4.1. 开发人员的一般反应是在使用相同代码的前提下,确保同时满足不同的需求。这就意味着需要添加可选参数、增补条件逻辑,以确保共享代码能够满足不同的需求

7.4.2. 使得实际代码更加复杂,甚至由此带来的问题比它解决的问题还要多

7.5. 与其试图把每一个逻辑都合并到同一段代码中,不如尝试使用独立的函数,这样也许会产生一些重复的代码

7.5.1. 函数可以做很多事情,但是它们只专注于完成一个任务,你应该用这个任务来给它命名

7.5.2. 如果你觉得在你的函数名称中有必要用连词“and”“or”,那么要么你的命名是错的,要么你把太多的职责寄托给这一个函数