单一职责原则
好的设计的一个最基本的原则就是:
把因相同原因变化的东西聚合到一起,把因不同原因变化的东西分离开来。
这个原则就是“单一职责原则”或者SRP。简短地说,即一个子系统、模块、类甚至是一个函数,就应该因多于一个的原则而变化。一个有着处理业务规则、报告和数据库的方法的类是经典的例子:
public class Employee { public Money calculatePay() ... public String reportHours() ... public void save() ... }
有些程序员可能认为将这三个函数一起放到同一个类中是再合适不过了的,毕竟类就应该是操作公共变量的函数的集合。但问题是这三个函数是因为完全不同的原因变化。每当计算支付额的业务规则改变时,calculatePay函数会变化。每当有人需要一个不同的报告格式时,reportHours函数会变化。每当DBA改变数据库的模式时,save函数就会变化。这三种变化原因的组合会让Employee变化无常,可能会因为这些原因中的任何一个而改变。更重要的是,任何依赖于Employee的类也会受到这些变化的影响。
好的系统设计意味着将系统分离成可以独立部署的组件。独立部署意味着如果我们修改了一个组件,不需要去重新部署其它的。然而,如果Employee被其它组件中的类大量使用,那么Employee中的任何一个改变都可能导致其它组件的重新部署,因而抵消掉组件设计(更流行的名字是“SOA”)的主要优点。
public class Employee { public Money calculatePay() ... } public class EmployeeReporter { public String reportHours(Employee e) ... } public class EmployeeRepository { public void save(Employee e) ... }
上面简单的划分就能解决这个问题。每个类都可以放置到它自己的组件中。更确切地说,所有的报告类都可以放到报告组件中,所有的数据库相关类可以放进存储组件,所有的业务规则可以放进业务规则组件中。
精明的读者可以看到,上面的解决方案中仍有依赖性,即Employee仍然被其它类所依赖。因而如果Employee被修改了,其它的类也可能需要重新编译或者重新部署。因而,Employee不能够被修改、不能独立布置。但其它两个类是可以修改和独立部署的,不会有一个的修改导致另一个重新编译或者重新部署的情况。即便是Employee类也可以通过谨慎地使用“依赖倒置原则”(DIP)来独立部署,但这是另一本书的主题了。
仔细地应用DRP,分离因不同原因变化的东西,是创建组件可独立部署的架构的关键。