深刻剖析软件设计决策中的权衡与取舍,涵盖单体系统、微服务、大数据处理等多领域。 通过真实案例与代码片段,展示软件设计模式的实际应用与错误决策的教训。 深分析软件设计中的潜在问题与局限,提前预防未来可能出现的陷阱。 通过预识别设计问题,减少后期修改与重构的成本。 阐释如何平衡灵活性与复杂性、性能与优化等关键设计要素。 提供一套系统化的方法,帮助软件工程师在有限资源下做出更明智的决策。
售 价:¥
纸质售价:¥94.80购买纸书
温馨提示:数字商品不支持退换货,不提供源文件,不支持导出打印
为你推荐
内容提要
献 词
前 言
致 谢
本书介绍
作者介绍
本书封面插图介绍
资源与支持
第1章 引言
1.1 决策的后果与模式
1.1.1 单元测试
1.1.2 单元测试与集成测试的比例
1.2 设计模式及其失效分析
1.3 架构设计模式及其失效分析
1.3.1 可扩展性与弹性
1.3.2 开发速度
1.3.3 微服务的复杂性
小结
第2章 代码重复不一定是坏事:代码重复与灵活性的权衡
2.1 代码库间的通用代码及重复代码
2.1.1 添加新需求导致的代码重复
2.1.2 实现新的业务需求
2.1.3 结果评估
2.2 通过库在代码库之间共享代码
2.2.1 共享库的取舍与不足
2.2.2 创建共享库
2.3 抽取代码为一个独立的微服务
2.3.1 采用独立微服务方式的取舍与弊端
2.3.2 关于独立微服务的总结
2.4 通过代码重复改善松耦合
2.5 利用继承减少API设计中的重复
2.5.1 抽取出一个请求处理器作为基类
2.5.2 继承与紧耦合的取舍
2.5.3 继承与组合的取舍
2.5.4 一贯性的重复与偶然性的重复
小结
第3章 异常及其他——代码错误的处理模式
3.1 异常的层次结构
3.2 代码异常处理的最佳模式
3.2.1 公共API的已检测异常处理
3.2.2 公共API的未检测异常处理
3.3 异常处理的反模式
3.3.1 异常时,关闭资源
3.3.2 反模式:利用异常控制应用流
3.4 源自第三方库的异常
3.5 多线程环境中的异常
3.6 使用Try以函数式的途径处理异常
3.6.1 在生产代码中使用Try
3.6.2 混合使用Try与抛出异常的代码
3.7 异常处理策略的性能对比
小结
第4章 灵活性与复杂性的权衡
4.1 一个健壮但无法扩展的API
4.1.1 设计一个新组件
4.1.2 从最简单的代码开始
4.2 允许客户使用自己的指标框架
4.3 通过钩子为你的API提供可扩展性
4.3.1 防范钩子API的过度使用
4.3.2 钩子API的性能影响
4.4 通过侦听器为你的API提供可扩展性
4.4.1 使用侦听器与钩子的取舍
4.4.2 设计的不可修改性
4.5 API的灵活性分析及维护开销的权衡
小结
第5章 过早优化vs热路径优化:影响代码性能的决策
5.1 过早优化是万恶之源
5.1.1 构建账户处理管道
5.1.2 依据错误的假设进行优化处理
5.1.3 对性能优化进行基准测试
5.2 代码中的热路径
5.2.1 从软件系统的角度理解帕累托法则
5.2.2 依据SLA配置线程(并发用户)数
5.3 具有潜在热路径的单词服务
5.3.1 获取每日一词
5.3.2 验证单词是否存在
5.3.3 使用HTTP服务,向外提供单词服务
5.4 检测代码中的热路径
5.4.1 使用Gatling创建API的性能测试
5.4.2 使用MetricRegistry度量代码路径
5.5 改进热路径的性能
5.5.1 为现有代码创建JMH基准测试
5.5.2 利用缓存优化word-exists程序
5.5.3 调整性能测试,使用更多的输入单词
小结
第6章 API的简洁性vs维护成本
6.1 一个为其他工具服务的基础库
6.1.1 创建云服务客户端
6.1.2 漫谈认证策略
6.1.3 理解配置的机制
6.2 直接暴露依赖库的配置
6.3 一个将依赖库的配置抽象化的工具
6.4 为云服务客户端库添加新的配置
6.4.1 为批处理工具添加新配置
6.4.2 为流处理工具添加新配置
6.4.3 方案对比:用户体验的友好性vs维护成本
6.5 弃用/删除云服务客户端库的某个配置
6.5.1 删除批处理工具的某个配置
6.5.2 删除流服务中某个配置
6.5.3 两种方案用户体验与维护成本的比较
小结
第7章 高效使用日期和时间数据
7.1 日期和时间信息的概念
7.1.1 机器时间:时间戳、纪元以及持续时间
7.1.2 民用时间:日历系统、日期时间以及期间
7.1.3 时区、UTC以及UTC偏移量
7.1.4 让人头疼的日期和时间概念
7.2 准备处理日期和时间信息
7.2.1 对范畴做界定
7.2.2 澄清日期和时间的需求
7.2.3 使用恰当的库或者包
7.3 实现日期和时间代码
7.3.1 保持概念的一致性
7.3.2 通过避免使用默认值提升可测试性
7.3.3 以文本方式表示日期和时间
7.3.4 通过注释解释代码
7.4 有必要单独指出并测试的极端情况
7.4.1 日历计算
7.4.2 发生在午夜时分的时区转换
7.4.3 处理不明确或者跳过的时间
7.4.4 处理不断变化的时区数据
小结
第8章 利用机器的数据本地性和内存
8.1 数据本地性是什么
8.1.1 将计算移动到数据处
8.1.2 用数据本地性扩展数据处理
8.2 数据的分区
8.2.1 线下大数据分区
8.2.2 分区和分片的区别
8.2.3 分区算法
8.3 连接多个分区上的大数据集
8.3.1 在同一台物理机上连接数据
8.3.2 需要数据移动的连接
8.3.3 利用广播优化连接
8.4 在内存还是磁盘中进行数据处理的权衡
8.4.1 基于磁盘的处理
8.4.2 我们为什么需要映射-化简?
8.4.3 计算访问时间
8.4.4 基于内存的处理
8.5 用Apache Spark实现连接
8.5.1 不使用广播的连接
8.5.2 使用广播的连接
小结
第9章 第三方库:你所用的库将成为你的代码
9.1 引用一个库就要对它的配置选项负责:小心那些默认配置
9.2 并发模型和可扩展性
9.2.1 使用异步和同步API
9.2.2 分布式的可扩展性
9.3 可测试性
9.3.1 测试库
9.3.2 用伪造值和模拟函数来进行测试
9.3.3 集成测试工具包
9.4 第三方库的依赖
9.4.1 避免版本冲突
9.4.2 太多的依赖
9.5 选择和维护第三方依赖
9.5.1 第一印象
9.5.2 复用代码的不同方式
9.5.3 锁定供应商
9.5.4 软件许可证
9.5.5 库和框架
9.5.6 安全和更新
9.5.7 选择第三方库的检查列表
小结
第10章 分布式系统的一致性和原子性
10.1 数据源的至少一次传输语义
10.1.1 单节点服务之间的网络访问
10.1.2 应用程序重试请求
10.1.3 生成数据和幂等性
10.1.4 理解CQRS
10.2 去重库的简单实现
10.3 在分布式系统里实现去重会遇到的常见错误
10.3.1 单节点环境
10.3.2 多节点环境
10.4 用原子性的逻辑避免竞争条件
小结
第11章 分布式系统的传输语义
11.1 事件驱动应用程序的架构
11.2 基于Apache Kafka的生产者和消费者应用程序
11.2.1 Kafka消费者
11.2.2 理解Kafka broker设置
11.3 生产者的逻辑
11.4 在消费者端实现不同的传输语义
11.4.1 消费者手动提交
11.4.2 从最早或最晚的偏移量开始重启
11.4.3 (最终)恰好一次传输语义
11.5 用传输保证提供容错能力
小结
第12章 版本管理和兼容性
12.1 版本管理的抽象思考
12.1.1 版本的属性
12.1.2 向后兼容性和向前兼容性
12.1.3 语义版本规范
12.1.4 营销版本
12.2 库的版本管理
12.2.1 源码、二进制和语义兼容性
12.2.2 依赖图和菱形依赖
12.2.3 处理破坏性改动的技术手段
12.2.4 管理内部库
12.3 网络API的版本管理
12.3.1 网络API调用的环境
12.3.2 用户喜欢公开透明的版本策略
12.3.3 常见的版本策略
12.3.4 版本管理的其他考虑因素
12.4 数据存储的版本管理
12.4.1 简要介绍Protobuf
12.4.2 哪些是破坏性改动
12.4.3 在存储系统内部迁移数据
12.4.4 准备好面对未知
12.4.5 分离网络API和存储的数据格式
12.4.6 评估存储格式
小结
第13章 紧跟最新技术趋势和维护旧代码之间的权衡
13.1 什么时候应该使用依赖注入框架
13.1.1 DIY依赖注入
13.1.2 使用依赖注入框架
13.2 什么时候应该使用响应式编程
13.2.1 创建一个单线程阻塞式处理模型
13.2.2 使用CompletableFuture
13.2.3 实现一个响应式方案
13.3 什么时候应该使用函数式编程
13.3.1 用非函数式语言写函数式代码
13.3.2 尾部递归优化
13.3.3 利用不可变性
13.4 对比延迟和急切初始化
小结
买过这本书的人还买过
读了这本书的人还在读
同类图书排行榜