《敏捷软件开发》之原则

简介

这段时间趁着学习《Head First设计模式》又抓紧时间把之前学习的《敏捷软件开发》复习了一下,但不得不承认,这两本书对设计模式有较深的深入,但相比于《Head First设计模式》《敏捷软件开发》更吸引我的一点是,这本书对实践这一块做的比较好,将原则、模式和实践结合得恰到好处。比如说:素数、保龄球、冒泡排序和薪水支付的案例,都是不错例子和场景。

回顾

敏捷宣言、极限编程、TDD测试、重构

测试驱动开发与重构的思考

敏捷设计

源代码是软件系统的主要设计文档

  • 我们不应该认为软件设计就是一组和代码分离的UML图,UML图只是描述了设计的一部分。
  • 软件项目的设计是一个抽象的概念。它和程序的概括形状、结构以及每一个模块、类和方法的详细形状和结构有关。
  • 设计的最终体现在 源代码 中。

设计中臭味—腐化软件的气味

  • 僵化性: 耦合性高,模块组件件关系太紧密,依赖性高,一动全动。
  • 脆弱性: 改动一处,其他相关联的地方也会变动,引发意想不到的问题。
  • 牢固性: 设计难以重用。
  • 粘滞性: 这一点有点难以理解。
  • 不必要的复杂性: 导入不必要的模块,组件或包。
  • 不必要的重复: 随意copy,将可以的抽象隐藏起来,而且如果随处copy的代码需要变动,得到处进行修改。
  • 晦涩性: 模块难以理解。

保持尽可能好的设计

  • 需求是项目中最不稳定的要素。
  • 敏捷团队依靠变化来获取活力。
  • 团队几乎不进行预先设计,因此不需要一个成熟的初始设计。
  • 敏捷开发人员致力于保持设计尽可能的适当、干净、简单,并且使用许多的单元测试和验收测试支援。
  • 敏捷软件开发知道要做的事情:
    • 遵循敏捷实践去发现问题
    • 应用设计原则去诊断问题
    • 应用适当的设计模式去解决问题

定义

敏捷设计是一个过程,不是一个事件。它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程。它致力于保持系统设计在任何时间都尽可能的简单、干净以及富有表现力。

臭味

  • 设计中的臭味是一种症状,是可以主观进行度量的。这些臭味通常是由违反了这些原则中的一个或者多个导致的。
  • 比如违反了开闭原则,会导致僵化性。
  • 应用原则的目的是去除僵化性,当没有臭味时就不应该应用这些原则

设计原则

单一职责原则(The Single Responsibility Principle)

内聚性

一个模块的组成元素之间的功能相关性。把内聚性和引起一个模块或者类改变的作用力联系起来。

职责

变化的原因

定义

就一个类而言,应该仅用一个引起它变化的原因。

描述

  • 每一个职责都是变化的一条轴线。当需求变化时,该变化反应为类的职责的变化。如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。
  • 如果一个类承担的职责过多,就等于把这些职责耦合在一起。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
  • 这种耦合导致脆弱的设计,当变化发生时,设计会遭受意想不到的破坏。

例子

  • Employee类包含了业务规则和对于持久化的控制。
  • 业务规则往往会频繁的变化,而持久化的方式却不会如此频繁的变化。
  • 测试驱动开发的实践常常会远在设计出现臭味之前就迫使我们分离这两个职责。

应用

  • SpringIOC源码中BeanFactory的设计,getBaen()和getBeanDefinition()、registerBeanDefinition()的分离。

开放、封闭原则(The Open-Close Principle)

定义

软件实体(类,模块、函数等等)应该是可以扩展的,但是不可修改的。

描述

对于扩展是开放的、对于更改是封闭的

本质

构造抽象来隔离引起的变化

结论

在许多方面,OCP都是面向对象设计的核心所在,遵循这个原则可以带来巨大好处。 比如,灵活性,可重用性以及可维护性。

Liskov替换原则(The Liskov Substitution Principle)

定义

子类型必须能够替换掉它们的基类型

描述

若对每个类型S的对象o1,都存在一个类型T的对象o2,使得在所有针对T编写的程序P中,用o1替换o2后,程序P行为功能不变,则S是T的子类型。

结论

一个模型,如果孤立地看,并不具有真正意义上的有效性。在考虑一个特定设计是否恰当时,不能完全孤立地来看这个解决方案。必须要根据该设计的使用者所做出的合理假设来审视它。

基于契约式设计

  • 约定大于配置
  • 契约:是通过为每个方法声明的前置条件和后置条件来指定的。
  • 要使一个方法得以执行,前置条件必须为真。执行完毕后,该方法要保证后置条件为真。

接口隔离原则(The Interface Segregation Interface)

定义

不应该强迫客户依赖它们不用的方法

胖类

意味着高耦合性,当一个客户程序要求该胖类进行一个改动时,会影响到所有其他的客户程序。

分离客户就是分离接口

分离接口的两种方式

  • 使用委托分离接口
  • 使用多重继承分离接口

依赖倒置原则(The Dependency Inversion Principle)

定义

高层模块不应该依赖于低层模块。两者都应该依赖于抽象。抽象不应该依赖于细节。细节应该依赖于抽象。

设计的层次化

  • 所有结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供了一组内聚的服务。
  • 这里的倒置不仅仅是依赖关系的倒置,它也是接口所有权的倒置。

程序中所有的依赖关系都应该终止于抽象类或者接口

  • 任何变量都不应该持有一个指向具体类的指针或者引用。
  • 任何类都不应该从具体类派生。
  • 任何方法都不应该覆写它的任何基类中的已经实现了的方法。