第1章:敏捷实践
本章探讨了敏捷运动的起源和核心价值观,旨在解决传统重量级流程带来的问题。
敏捷宣言的四大价值观:- 个体和互动 高于 流程和工具。
- 工作的软件 高于 详尽的文档。
- 客户合作 高于 合同谈判。
- 响应变化 高于 遵循计划。
- 我们的最高目标是,通过尽早和持续地交付有价值的软件来满足客户。
- 欢迎对需求提出更改,即使在开发后期。敏捷过程利用变化为客户创造竞争优势。
- 工作的软件是进度的首要度量标准。
- 敏捷过程提倡可持续的开发速度。责任人、开发者和用户应该能够保持一个长期的、稳定的开发速度。
- 简洁——最大化未完成工作的艺术——是根本。
- 最好的架构、需求和设计出自自组织的团队。
第2章:极限编程概览 (XP)
XP是敏捷方法中最著名的一种,由一系列相互依赖的实践组成。
- 客户作为团队成员: 客户全程参与,定义和排序用户故事。
- 短周期: 以一到两周为迭代周期,频繁交付可工作的软件。
- 测试驱动开发 (TDD): 先写失败的单元测试,再编写恰好能让测试通过的代码,最后重构。
- 结对编程: 所有生产代码都由两人在同一台电脑上共同完成,以提高代码质量和知识共享。
- 重构: 持续地改进代码内部结构,而不改变其外部行为,以对抗代码腐化。
- 简单设计: 永远选择“能工作的最简单方案” (YAGNI - You Ain't Gonna Need It)。
- 持续集成: 团队成员每天多次集成他们的工作。
第3-6章:规划、测试、重构与编程实例
这几章通过一个保龄球计分程序的实例,生动展示了XP实践的威力。
核心观点:设计的演化应由测试驱动,而不是预先过度设计。保龄球计分程序最初并不需要`Game`、`Frame`、`Throw`等复杂对象,一个简单的整数数组就足以开始。随着测试用例的增加(如处理全中和补中),设计才逐步演化得更加完善。这体现了简单设计和演进式设计的思想。
第7章:什么是敏捷设计?
敏捷设计是一个持续的过程,而非一个阶段。核心观点是:源代码就是设计。
糟糕设计的“坏味道” (Smells of Rotting Software):- 僵化性 (Rigidity): 难以修改,一个改动会导致连锁反应。
- 脆弱性 (Fragility): 修改代码时,容易在不相关的地方引入新错误。
- 顽固性 (Immobility): 难以将代码复用到其他系统中。
- 粘滞性 (Viscosity): 做正确的事比做错误的事更难。
- 不必要的复杂性 (Needless Complexity): 过度设计。
- 不必要的重复 (Needless Repetition): 复制粘贴代码。
- 晦涩性 (Opacity): 代码难以理解。
敏捷团队通过持续的重构来对抗这些“坏味道”,让设计始终保持整洁、简单和富有表现力。
SOLID原则是面向对象设计的基石,旨在创建易于维护和扩展的系统。
- S - 单一职责原则 (SRP):
一个类应该只有一个引起它变化的原因。本章通过`Rectangle`类的例子(几何计算职责 vs. GUI绘制职责)说明了将不同变化轴向的职责分离开的重要性。
- O - 开放-封闭原则 (OCP):
软件实体(类、模块、函数等)应该对扩展是开放的,但对修改是封闭的。抽象是实现该原则的关键。当需求变化时,我们应该通过添加新代码(如实现一个新的子类)来应对,而不是修改已有的、工作的代码。
- L - 里氏替换原则 (LSP):
子类型必须能够替换掉它们的基类型。著名的“正方形是矩形吗?”问题揭示了LSP的精髓:继承关系必须基于行为,而不仅仅是属性。如果子类的行为与客户端对基类的期望不符,那么继承就是不恰当的。
- I - 接口隔离原则 (ISP):
客户端不应该被强迫依赖于它们不使用的方法。该原则提倡使用多个小的、专一的接口(角色接口),而不是一个大的、臃肿的接口(胖接口),以避免不必要的耦合。
- D - 依赖倒置原则 (DIP):
A. 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
B. 抽象不应该依赖于细节,细节应该依赖于抽象。
这是传统分层架构的“反转”,是框架设计的核心。通过依赖于接口而非具体实现,系统变得更加灵活和可复用。
第13-22章:薪资系统设计与打包
这个案例研究详细展示了如何将设计原则和模式应用于一个实际问题。从一个简单的需求开始,通过迭代逐步构建出一个健壮、灵活的薪资系统。
核心设计决策与模式应用:- 事务处理: 使用命令模式 (COMMAND) 将“添加员工”、“删除员工”等操作封装为事务对象。这实现了操作的请求者与执行者的解耦。
- 支付分类/方式/时间表: 员工有不同的支付方式(小时工、月薪、提成),不同的支付方法(邮寄、直接存款),不同的支付周期(每周、每月)。这些易变的部分被策略模式 (STRATEGY) 完美地分离开来,使得`Employee`类对这些变化封闭。
- 添加员工事务: `AddEmployeeTransaction`的执行逻辑是固定的,但获取具体支付分类和时间表的逻辑是变化的。这正是模板方法模式 (TEMPLATE METHOD) 的经典应用场景。
- 数据库访问: 使用外观模式 (FACADE) 创建一个`PayrollDatabase`类,将所有数据库操作的复杂性封装起来,为上层提供一个简单的接口。
- 单一实例: 对于像数据库这样的全局访问点,可以使用单例模式 (SINGLETON) 或MONOSTATE模式来确保其唯一性。
- 处理“空”情况: 员工可以没有工会会籍。与其在代码中到处检查`null`,不如使用一个`NoAffiliation`对象,这就是空对象模式 (NULL OBJECT)。
- 对象创建: 随着系统包的划分,为了避免高层包依赖于低层具体类,引入了工厂模式 (FACTORY),由工厂负责创建具体实例,从而遵循了依赖倒置原则。
- 包内聚性原则:
- 复用-发布等价原则 (REP): 复用的粒度就是发布的粒度。
- 共同复用原则 (CRP): 一个包中的类应该被一起复用。
- 共同封闭原则 (CCP): 一个包中的类应该对同一种变化共同封闭。
- 包耦合性原则:
- 无环依赖原则 (ADP): 包依赖关系图中不应该有环。
- 稳定依赖原则 (SDP): 依赖应该指向更稳定的方向。
- 稳定抽象原则 (SAP): 包的抽象程度应该和其稳定程度一致。
第23-27章:气象站案例
这个案例展示了如何从一个紧密耦合的设计,通过重构,“退后”到一个经典的模式中。核心在于分离UI、业务逻辑和硬件API。
- 初始问题: `Scheduler`(调度器)了解所有的传感器和UI组件,导致任何组件的增删都会修改`Scheduler`,违反了OCP。
- 演化过程:
- 引入观察者模式 (OBSERVER),让UI观察传感器,从而将UI从调度器中解耦。
- 随着需求增多(如多个UI、传感器间依赖),设计进一步演化,最终形成了标准的“主题(Subject)-观察者(Observer)”结构。
- 使用桥接模式 (BRIDGE) 和工厂模式 (FACTORY) 将硬件抽象(API)与业务逻辑(传感器)彻底分离,实现了跨平台。
第28-30章:ETS框架案例
该案例聚焦于为一个考试系统构建一个可复用的框架,展示了如何处理对一个稳定类层次结构添加新功能的需求。
- 访问者模式 (VISITOR): 当需要为一个稳定的类层次结构(如`Modem`及其子类)添加新操作(如为不同操作系统配置),但又不希望修改这些类时,可以使用访问者模式。它将操作逻辑从对象结构中分离出来。
- 非循环访问者 (ACYCLIC VISITOR): 解决了传统访问者模式中,增加新`Modem`子类会导致`ModemVisitor`接口改变的循环依赖问题。
- 状态模式 (STATE): 用于实现复杂的有限状态机(FSM)。通过将每个状态封装成一个对象,状态转换变成了更换对象,而不是使用巨大的`switch-case`语句。这使得状态逻辑清晰且易于扩展。书中的地铁闸机和GUI任务控制器是绝佳示例。