Writing computer software is one of the purest creative activities in the history of the human race. Programmers aren’t bound by practical limitations such as the laws of physics; we can create exciting virtual worlds with behaviors that could never exist in the real world. Programming doesn’t require great physical skill or coordination, like ballet or basketball. All programming requires is a creative mind and the ability to organize your thoughts. If you can visualize a system, you can probably implement it in a computer program.
编写计算器软件,是人类历史上最纯洁的创造性活动之一。程序员不会被现实中的条条框框所束缚(比如物理法则),我们可以创造振奋人心的、超脱于现实的虚拟世界。编程不需要类似于芭蕾舞或者篮球运动那样特殊的身体技巧或者群体配合,只需要充满创造力的思维和组织想法的能力。如果你能让一个系统可视化,那大概也能用程序去实现它。
This means that the greatest limitation in writing software is our ability to understand the systems we are creating. As a program evolves and acquires more features, it becomes complicated, with subtle dependencies between its components. Over time, complexity accumulates, and it becomes harder and harder for programmers to keep all of the relevant factors in their minds as they modify the system. This slows down development and leads to bugs, which slow development even more and add to its cost. Complexity increases inevitably over the life of any program. The larger the program, and the more people that work on it, the more difficult it is to manage complexity.
这意味着,编写程序最大的限制,就是理解当前创造中的系统的能力。当程序进化并获得更多的特性,它就变得复杂,与它的组件之间保持着微妙的相互依赖。随着时间推移,复杂度积累,在需要调整系统的时候,要让程序员记住这些相关因素就会越来越难。这样就让开发变得缓慢,并产生更多的 BUG,这些 BUG 又回减慢开发,恶性循环让开发成本增加。任何程序的开发周期中,复杂度都是逐渐增加的。越大的程序,需要越多人开发,控制复杂度也就越难。
Good development tools can help us deal with complexity, and many great tools have been created over the last several decades. But there is a limit to what we can do with tools alone. If we want to make it easier to write software, so that we can build more powerful systems more cheaply, we must find ways to make software simpler. Complexity will still increase over time, in spite of our best efforts, but simpler designs allow us to build larger and more powerful systems before complexity becomes overwhelming.
好的开发工具能帮我们控制复杂度,近十年来也诞生了不少这样的好工具。但是光靠这些工具,能做到事情也是有限的。如果想要让软件开发变得简单,从而让开发强大的系统变得方便,那么我们就必须让软件开发变得简单。复杂度仍在与日俱增,尽管我们已经尽力去把控了。更简单的设计,能让我们建造更大更强的系统。
There are two general approaches to fighting complexity, both of which will be discussed in this book. The first approach is to eliminate complexity by making code simpler and more obvious. For example, complexity can be reduced by eliminating special cases or using identifiers in a consistent fashion.
对抗复杂度有两种普遍方法,书上都会介绍到。第一种,是让代码更简单更浅显,比如减少特殊情况,或者在一致的代码风格中使用标识。
The second approach to complexity is to encapsulate it, so that programmers can work on a system without being exposed to all of its complexity at once. This approach is called modular design. In modular design, a software system is divided up into modules, such as classes in an object-oriented language. The modules are designed to be relatively independent of each other, so that a programmer can work on one module without having to understand the details of other modules.
第二种就是封装,这种方式让程序员开发时候不必一下子就看到那些复杂的内容。这种方法叫做模块化设计,把软件系统分成多个模块,在面向对象的语言中可以是类的形式。模块被设计成相互之间相对独立,这样开发一个模块就不需要去理解其他模块的细节了。
Because software is so malleable, software design is a continuous process that spans the entire lifecycle of a software system; this makes software design different from the design of physical systems such as buildings, ships, or bridges. However, software design has not always been viewed this way. For much of the history of programming, design was concentrated at the beginning of a project, as it is in other engineering disciplines. The extreme of this approach is called the waterfall model, in which a project is divided into discrete phases such as requirements definition, design, coding, testing, and maintenance. In the waterfall model, each phase completes before the next phase starts; in many cases different people are responsible for each phase. The entire system is designed at once, during the design phase. The design is frozen at the end of this phase, and the role of the subsequent phases is to flesh out and implement that design.
因为软件过于具备延展性,所以软件设计是一个持续性的进程,它跨域了软件系统的整个生命周期。这让软件开发有别于现实系统的设计,比如大楼、船只和桥梁这一类。然而,软件设计并不是一开始就以这样的观点所认识,从编程的历史来看,设计的工作更多地是在项目开发的初始阶段集中进行,就像其它的工程学科那样。这种方式的极端做法,叫做瀑布流模型,它把一个项目划分成多个阶段,比如需求评审、设计、开发、测试和维护。瀑布流模型之下,一个阶段完成另一个阶段才能开始,很多情况下不同的人负责不同的阶段。整个系统一次过设计成型,设计在它的阶段结束时就被固定下来了,后续的阶段就是构造并充实设计。
Unfortunately, the waterfall model rarely works well for software. Software systems are intrinsically more complex than physical systems; it isn’t possible to visualize the design for a large software system well enough to understand all of its implications before building anything. As a result, the initial design will have many problems. The problems do not become apparent until implementation is well underway. However, the waterfall model is not structured to accommodate major design changes at this point (for example, the designers may have moved on to other projects). Thus, developers try to patch around the problems without changing the overall design. This results in an explosion of complexity.
不幸的是,瀑布模型少有好的表现。软件系统实际上比物理系统更复杂。想要让大型软件系统充分地可视化,然后在构建之前就要充分地理解它的含义,是不可能的。结果就是,初版设计出现很多问题。因为直到需求被充分实现之前,问题都不会浮现。然而,在这个节骨眼上就会发现,瀑布流模型就不是被设计成能够容纳大型的整改的形式(比如设计者这时就已经被指派到了其它项目中)。所以,开发者就要千方百计在不改动整体设计的情况下,对产生的问题修修补补,最终就会迎来复杂度大爆发。
Because of these issues, most software development projects today use an incremental approach such as agile development, in which the initial design focuses on a small subset of the overall functionality. This subset is designed, implemented, and then evaluated. Problems with the original design are discovered and corrected, then a few more features are designed, implemented and evaluated. Each iteration exposes problems with the existing design, which are fixed before the next set of features is designed. By spreading out the design in this way, problems with the initial design can be fixed while the system is still small; later features benefit from experience gained during the implementation of earlier features, so they have fewer problems.
基于以上种种问题,今天的大多数软件开发项目都是用渐进式方式,比如敏捷开发,初始设计只关注于全部功能的一小部分。这部分子集会被设计、实现,然后评估,初版的问题就会被发现并整改,之后更多的小子集的功能也会被设计、实现再评估。每次迭代都能发现现有设计的问题,就能把它们在下一部分功能设计之前消灭掉。用这种扩散式设计方案,初版问题在系统还小的时候就被修复了。后面的功能在实现的时候,前面的修复就能成为经验,所以能比上一阶段出现更少的问题。
The incremental approach works for software because software is malleable enough to allow significant design changes partway through implementation. In contrast, major design changes are much more challenging for physical systems: for example, it would not be practical to change the number of towers supporting a bridge in the middle of construction.
渐进式开发的有效,是因为软件变得有足够的韧性,从而能在实现的过程中对设计进行大型调整。对比之下,大型设计整改对于物理系统来说就是困难重重的,比方说,想要在构建桥梁的过程中修改承重的柱子的数量,是不切实际的。
Incremental development means that software design is never done. Design happens continuously over the life of a system: developers should always be thinking about design issues. Incremental development also means continuous redesign. The initial design for a system or component is almost never the best one; experience inevitably shows better ways to do things. As a software developer, you should always be on the lookout for opportunities to improve the design of the system you are working on, and you should plan on spending some fraction of your time on design improvements.
渐进式开发意味着软件设计是永不停息的。在系统的生命周期中,设计就是连续进行的:开发者需要一直思考设计问题。渐进式开发同时意味着持续的重新设计,初版设计的组件对于系统来说永远都不是最好的一版,不可避免地,经验会告诉你更好的实现方式。作为开发者,必须一直留心,一有机会就改进手头上的设计,还要计划有空就改进一下设计。
If software developers should always be thinking about design issues, and reducing complexity is the most important element of software design, then software developers should always be thinking about complexity. This book is about how to use complexity to guide the design of software throughout its lifetime.
如果软件开发者能一直考虑设计问题,并认为减少复杂度是软件开发的第一要领,那么复杂度就是他们经常要思考的要素。本书就是关于用复杂度,去指引软件设计的生命周期中,设计应当如何走向。
This book has two overall goals. The first is to describe the nature of software complexity: what does “complexity” mean, why does it matter, and how can you recognize when a program has unnecessary complexity? The book’s second, and more challenging, goal is to present techniques you can use during the software development process to minimize complexity. Unfortunately, there isn’t a simple recipe that will guarantee great software designs. Instead, I will present a collection of higher-level concepts that border on the philosophical, such as “classes should be deep” or “define errors out of existence.” These concepts may not immediately identify the best design, but you can use them to compare design alternatives and guide your exploration of the design space.
本书有两个主要目的。第一,是阐述软件复杂度的性质:什么是复杂度,为什么它是关键,还有怎样才能看出程序有多余的复杂度。第二,也是更难的一点,软件开发过程中能使用并让复杂度最小化的技巧。不幸的是,没有简单易懂的配方能保证大型软件设计出来。取而代之的是,我会提供一系列接近于哲学的高阶理念,比如“类需要深度”或者“从未存在中定义出问题”。这些理念也许不能马上鉴别出最优设计,但是如果用它们对比你的候选设计方案,可以指引你探索关于设计的浩瀚大海。