diff --git "a/docs/study/\347\274\226\347\250\213\346\200\235\346\203\263/\343\200\212\344\273\243\347\240\201\345\244\247\345\205\2502\343\200\213-\350\257\273\344\271\246\347\254\224\350\256\260.md" "b/docs/study/\347\274\226\347\250\213\346\200\235\346\203\263/\343\200\212\344\273\243\347\240\201\345\244\247\345\205\2502\343\200\213-\350\257\273\344\271\246\347\254\224\350\256\260.md" index 0221fc3..c7fb860 100644 --- "a/docs/study/\347\274\226\347\250\213\346\200\235\346\203\263/\343\200\212\344\273\243\347\240\201\345\244\247\345\205\2502\343\200\213-\350\257\273\344\271\246\347\254\224\350\256\260.md" +++ "b/docs/study/\347\274\226\347\250\213\346\200\235\346\203\263/\343\200\212\344\273\243\347\240\201\345\244\247\345\205\2502\343\200\213-\350\257\273\344\271\246\347\254\224\350\256\260.md" @@ -1,5 +1,7 @@ # 《代码大全2》-读书笔记 +> 个人认为相对重要的章节,标注*(星号) + # 阅读指南 1. 初级程序员:优先18章 @@ -67,9 +69,177 @@ ## 3.3 问题定义的先决条件 +除非问题本身背景就是程序本身,否则尽量使用客户语言(非程序语言)表述问题本身,并且通常不需要夹带自认为的解决方案(初衷是好的,但是自认为的解决方案本身不一定是正确的或者合适的) + ## 3.4 需求的先决条件 -## 3.5 架构的先决条件 +开发前明确需求,减少分歧以及开发中额外增加工作量打乱节奏。 + +1. 确保每一个人都知道需求变更的代价(比如产品可能灵光一闪需要加改动,那么需要明确周知改动量和排期变动,而不是在现有排期无脑追加工作量) +2. 建立一套变更控制程序(遇到频繁变更需求要求的场景,和客户协商变更流程,而非一股脑接收随时提出的变更) +3. 使用能适应变更的开发方法(即有些时候可以先做一部分设计,然后和客户对齐需求,如果没有歧义,继续响应剩余的需求内容,完成设计) +4. 放弃这个项目(如果项目本身捉摸不定,那么可以协商取消) +5. **注意项目的商业案例**(如果需求本身没有带来商业收益,可以此为理由拒绝部分需求内容) + +## * 3.5 架构的先决条件 + ++ 程序组织 + 1. 架构需要有明确的主要的类,以及说明这些主要的类之间如何联系和工作 ++ 主要的类 + + 1. 架构应该给出选用当前架构的理由(这个比较理想,不乏使用新架构仅仅是KPI考核的情况) + + 1. 架构无需详细说明系统每一个类,但对于构成系统的80%行为的20%的类应该详细说明(也就是和整个架构执行流程强相关的重点类,应该详细说明,比如链式调用流程等等) ++ 数据设计 + + 1. 主要的文件和数据表的设计应该具体描述技术选型和原因(比如设计顺序访问的ID,那么需要比较顺序访问ID和随机访问ID在该场景下的优劣和选择原因) + 2. **数据通常只应该由一个子系统或者一个类直接访问**:例外的情况就是透过访问器类(access class)或访问器子程序(access routine)——以受控且抽象的方式来访问数据。(实际开发中一般也建议某一个MySQL表统一由某个类完成CRUD访问,或者只由某个RPC服务负责提供CRUD该数据表的接口) + 3. 架构应该详细定义所用数据库的高层组织结构和内容。应该解释为什么单个数据库比多个数据库好(反之亦然),解释为什么不用平坦的文件而是数据库,指出与其他访问统一数据的程序的可能交互方式(比如提供RPC接口等),说明创建哪些数据视图(view),等等。 ++ 业务规则 + + 1. 如果架构依赖于特定的业务规则,就应该详细描述这些业务规则,并描述规则对系统设计的影响。 ++ 用户界面设计 + + 1. 通常用户界面在需求阶段进行详细说明(一般指前端UI页面设计等,通常和前端有关,另外可能影响后端接口交互形式)。如果没有,则应该在软件架构中进行详细说明。 + + 2. 架构应该模块化,以便在替换成新用户界面时不影响业务规则和程序的输出部分。 ++ 资源管理 + + 1. **架构应该描述一份管理稀缺资源的计划。稀缺资源包括数据库连接,线程,句柄(handle)等**。 ++ 安全性 + + 1. 架构应该描述实现设计层面和代码层面的安全性的方法。(比如数据应该编码后再入库,避免XSS攻击等;SQL应该预编译处理避免SQL注入攻击;cookie内仅存储加密后的数据等等) ++ 性能 + + 1. 如果需要关注性能,需求中应详细定义性能目标。(比如接口耗时,内存使用,机器资源成本等) + 2. 架构应该提供估计的数据,并解释为什么能达到性能目标。(比如预估Redis使用的内存并申请资源) ++ 可扩展性(可伸缩性) + + 1. 架构设计时应考虑如何应对用户数量,服务器数量,网络节点数量,数据库记录数,数据库记录的长度,交易量的增长。 ++ 互用性 + + 1. 如果预计这个系统会和其他软件或硬件共享数据和资源,架构需描述如何完成该任务。(比如通过RPC统一收口某些资源的访问等等) ++ 国际化/本地化 + + 1. 交互系统,需要架构留意国际化问题,并说明如何设计实现以及原因(比如状态显示,异常文案,帮助信息等如何国际化,一般会开发一套内部的译文平台用于统一管理) ++ 输入/输出 + + 1. 输入输出(I/O)是架构中值得注意的另一个领域。架构应该详细定义读取策略 (reading scheme)是先做(look-ahead)、后做(look-behind)还是即时做(just-in-time)。 而且应该描述在哪一层检测I/O错误:在字段、记录、流,或者文件的层次。 ++ 错误处理 + + 1. 错误处理,是纠正还是仅检测(纠正比如超出分页范围,则采用指定的最大分页范围;检测即超出最大值直接抛出异常) + 2. 错误检测,主动还是被动(主动:比如界面在输入完后停顿1s检测输入合法性;被动:比如表单上传后,再对数据校验报错) + 3. 如何传播错误,忽略错误数据继续处理剩余数据;遇到错误则进入错误处理状态;等到所有处理结束后,统一汇总错误信息并返回 + 4. 错误消息处理的约定,架构应该建设一套有关错误消息的约定(比如后端统一的异常处理器,前端则采用相同样式的异常消息吐司提示框) + 5. **如何处理异常,架构应规定代码何时能抛出异常,什么地方捕获异常,如何记录log异常,以及如何在文档中描述异常** + 6. 在什么层次处理错误,发现异常的地方处理;错误传递到专门的处理类上;沿函数调用链向上传递错误 + 7. **每个类在验证输入的有效性方面该负何种责任,每个类单独负责验证自己的数据有效性,还是一组类负责验证整个系统数据的有效性,某个层级上的类是否能假设自己接收的数据是干净的(无错误的)**(举例子,在DDD领域开发模式下,很可能不同类针对不同业务场景对相同字段会有不同的校验规则,或者同一个字段的不同校验规则维护到某一个类中;还有就是不同业务各自针对同一个字段维护不同的业务校验逻辑,并且在业务调用链路上彼此不影响) + 8. 错误处理机制,采用运行环境内建的错误处理机制,或者自建一套机制(通常针对业务抛出的异常有统一的异常处理方式;而其他非主动捕获的运行时异常再用另一个统一处理器处理) ++ 容错性 + 1. 如果可能的话从错误中恢复;如果不能的话则包容其不利影响 ++ 架构的可行性 + 1. 性能,资源,网络环境等,架构需要论证系统的技术可行性 ++ 过度工程 + 1. 架构应该清楚地指出程序员应该“为了谨慎起见宁可进行过度工程”,还是应该作出最简单的能工作的东西 ++ 买or造轮子的决策 + 1. 架构如果不采用现货供应的组件, 那么应该说明“自己定制的组件应在哪些方面胜过现成的程序库和组件”。 ++ 关于复用的决策 + 1. 如果复用已存在的软件、测试用例、数据格式或其他原料,架构应该说明:如何对复用的软件进行加工,使之符合其他架构目标 ++ 变更策略 + 1. 架构应该尽量灵活,以适应可能出现的变化 + 2. 架构应该清楚地描述处理变更的策略,尽可能料想可能的变化以及应对方案(比如增加版本字段,或对代码生成器只需简单修改) + 3. 架构应提出“延迟提交”所用的策略(比如或许该规定使用表驱动技术,规定表保存在外部文件,而非直接硬编码代码,或许能做到不重新编译的情况下修改程序,等等) ++ 架构的总体质量 + 1. 架构不应该包含任何仅仅为了取悦老板的东西 + 2. 架构不应该包含任何对自己而言很难理解的东西 + 3. 架构只包含需要的部分,不为了镀金而做多余的部分 ## 3.6 花费在前期准备上的时间长度 +一个良好运作的项目会在需求、架构以及其他前期计划方面投入10%~20%的工作量和20%~30%的时间。如果有必要,架构工作也可以作为独立的项目来对待(比如遗老项目整体维护成本特别高,可能需要考虑小组一同按照新架构进行业务迁移) + +# 第4章 关键的"构建"决策 + +## 4.1 选择编程语言 + +## 4.2 编程约定 + +在"构建"开始之前,讲清楚使用的编程约定。编程约定细节要达到这样的精确度:在编写完软件之后,几乎不可能改变(翻新)软件所遵循的编码约定。 + +## 4.3 你在技术浪潮中的位置 + +## 4.4 选择主要的构建实践方法 + ++ 每种编程语言都有其优点和弱点。要知道你使用的语言的明确优点和弱点。 ++ **在开始编程之前,做好一些约定(convention)。“改变代码使之符合这些约定”是近乎不可能的。** ++ “构建的实践方法”的种类比任何单个项目能用到的要多。有意识地选择最适合你的项目的实践方法。 ++ 问问你自己,你采用的编程实践是对你所用的编程语言的正确响应,还是受它的控制?请记得“深入一种语言去编程”,不要仅“在一种语言上编程”。 ++ 你在技术浪潮中的位置决定了哪种方法是有效的——甚至是可能用到的。确定你在技术浪潮中的位置,并相应调整计划和预期目标。 + +# 第2部分 创建高质量的代码 + +# 第5章 软件构建中的设计 + +## 5.1 设计中的挑战 + +## 5.2 关键的设计概念 + +高质量的设计具有很多常见的特征,常见的有: + +1. 最小的复杂度 +2. 易于维护 +3. 松散耦合 +4. 可扩展性 +5. 可重用性 +6. 高扇入(大量的类使用某个给定的类,系统很好地利用了较低层级上的工具类) +7. 低扇出(一个类中少量或适中地使用其他的类) +8. 可移植性 +9. 精简性 +10. 层次性 +11. 标准技术 + +## * 5.3 设计构造块: 启发式方法 + +查阅常用的设计模式:适配器,桥接,装饰器,外观,工厂方法,观察者,单件,策略,模板方法。 + +常见的设计模式 + +| 模式 | 描述 | +| -------- | ------------------------------------------------------------ | +| 抽象工厂 | 通过指定对象组的种类而非单个对象的类型来支持创建一组相关的对象 | +| 适配器 | 把一个类的接口转变成另一个接口 | +| 桥接 | 把接口和实现分离开来,使它们可以独立地变化 | +| 组合 | 创建一个包含其他同类对象的对象,使得客户代码可以与最上层对象交互而无须考虑所有的细节对象 | +| 装饰器 | 给一个对象动态地添加职责,而无须为了每一种可能的职责配置情况去创建特定的子类(派生类) | +| 外观 | 为没有提供一致接口的代码提供一个一致的接口 | +| 工厂方法 | 做特定基类的派生类的实例化时,除了在Factory Method内部之外的均无须了解各派对象的具体类型 | +| 迭代器 | 提供一个服务对象来顺序地访问一组元素中的各个元素 | +| 观察者 | 使一组相关对象相互同步,方法是让另一个对象负责:在这组对象中的任何一个发生改变时,由它把这种变化通知给这个组里的所有对象 | +| 单例 | 为有且仅有一个实例的类提供一种全局访问功能 | +| 策略 | 定义一组算法或行为,使得它们可以动态地相互替换 | +| 模板方法 | 定义一个操作的算法结构,但是把部分实现的细节留给子类(派生类) | + +## 5.4 设计实践 + +## 5.5 对流行的设计方法的评论 + +# 第6章 可以工作的类 + +## 6.1 类的基础: 抽象数据类型(ADTs) + +## 6.2 良好的类接口 + +## 6.3 有关设计和实现的问题 + +## 6.4 创建类的原因 + +## 6.5 与具体编程语言相关的问题 + +## 6.6 超越类: 包 + +# 第7章 高质量的子程序 + +# 第8章 防御式编程 + +# 第9章 伪代码编程过程 +