本书展示了一系列设计软件时可能遇到的现实问题,试图对软件设计中可能出现的各种情况进行分析,并逐一解说每种决策的权衡与取舍。本书也会讲解一些并不常见的软件设计缺陷和错误,这些缺陷和错误可能会对你的软件系统产生方方面面的影响而不仅是影响程序的正确性。
本书适合希望理解软件系统设计的取舍与常见设计模式的软件工程师阅读。同时,本书开篇从基础性的话题切入,介绍如何避免常见的设计缺陷,对刚刚开启职业生涯的软件工程师而言,也是大有裨益的。接着,本书转入相对深入的话题,即便是有经验的程序开发者也能从中获益。本书使用的主开发语言是Java,案例、模式以及代码片段都基于Java,不过对软件设计的决策并不局限于Java语言。
本书共13章。第1章概述本书使用的决策分析方法。其余各章相对独立,分别专注于软件工程的不同方面。为了从本书中获得最大的收益,建议你按顺序阅读各章的内容。但是,如果你对软件工程的某个方面感兴趣,可以直接跳转到对应章节。
■ 第1章介绍软件决策分析时所采用的方法。我们会从软件架构、代码以及质量保证等方面,举例说明如何做出权衡与取舍。
■ 第2章阐释代码重复不一定是坏事。本章从不同的架构角度出发,分析代码重复如何对系统的松耦合或紧耦合造成影响;并且使用阿姆达尔定律计算团队内部协调与跨团队协同的开销。
■ 第3章描述代码出现异常情况时的处理模式。我们会按照已检测异常和未检测异常两类用例分别展开介绍。本章还介绍如何为公共API(库)设计异常处理策略。最后,我们探讨面向对象的程序设计与函数式编程方法在错误处理时的权衡与取舍。
■ 第4章介绍如何平衡代码以及API设计中的灵活性和复杂性。通常情况下,代码在一个方向上的演进会对其在另一个方向上的发展造成影响。
■ 第5章告诉我们,在项目的早期开展优化并不是坏事。使用适当的工具和定义恰当的SLA,我们可以发现代码路径中的热路径并对其进行优化。此外,本章还演示如何利用帕累托法则帮助定位系统中适合进行优化的部分,从而将优化工作聚焦于此。
■ 第6章介绍如何设计对用户体验友好的API。通过本章的介绍,我们会了解设计对用户体验友好的API不仅是UI的事,也需要编程接口的支持,譬如REST API、命令行工具以及其他接口。然而,这也表明,为了获得良好的用户体验,我们需要付出更高的维护成本。
■ 第7章讨论如何处理日期和时间数据,这是一个极其棘手的问题。想想我们的数据里有多少日期和时间元素,譬如出生日期或者日志的时间戳,这些地方都极有可能出现错误。这并不是一个复杂的领域,但确实需要我们特别注意。
■ 第8章介绍为什么数据本地性在大数据处理中至关重要,还介绍分配数据和流量的分区算法要满足哪些要求。
■ 第9章介绍将你使用的库变成你的代码。本章会讨论将第三方库导入代码库时需要考虑的因素、可能引发的问题及权衡与取舍。最后,本章试图回答一个问题:我们应该导入一个库还是重新实现它的一小部分。
■ 第10章重点讨论设计分布式系统时一致性与原子性之间的权衡。本章分析分布式系统中可能发生的竞争条件,并展示幂等性是如何影响我们设计系统的方式的。
■ 第11章介绍如何处理分布式系统中的传输语义。本章可以帮助读者理解分布式系统中“至少一次”、“至多一次”和“最终恰好一次”的含义。
■ 第12章介绍软件、API以及存储数据是如何随着时间的推移发展、演进的,并介绍它们如何能在保持与其他系统的兼容性的同时做到这些。
■ 第13章讨论紧跟IT行业的最新技术趋势可能并不总是明智的选择。本章分析一些广泛使用的模式和框架,譬如响应式编程,也对这些技术在某些特定场景的适用性进行讨论。
本书包含大量的代码示例,有的是以数字序号标注的代码清单的方式呈现的,有的则是以普通文本的方式呈现的。有些时候代码会被 加粗 从而区别于之前的代码,譬如,对一行现存代码进行修改,增加了新的功能。
很多时候,源码会被重新格式化,我们会添加换行符或者对代码进行重构,引入代码缩进以适配页面的可用空间。即便如此,还是有一些极端的情况,代码清单中会包含行连续标记(➥)。此外,如果代码在正文中有介绍,源码中不再添加注释。
本书示例的源码根据谷歌代码指南,使用自动化插件进行了格式化。许多代码清单都附有代码注释,对重要的概念进行强调。为了保证代码质量,本书使用的所有代码都有大量的单元测试和集成测试。但并不是所有的测试都在本书的代码清单中进行了展示。你可以阅读并运行这些测试从而更深入地理解某部分的逻辑。通过阅读代码库中的README.md文件,你可以了解如何导入并运行这些示例代码。本书示例的完整代码可以从https://github.com/tomekl007/ manning_software_mistakes_and_tradeoffs下载,也可按“资源与支持”页指引,在异步社区下载。