单一职责原则(Single Responsibility Principle,SRP)最初是谁提出的我没有考证,不过大师Robert C. Martin对SRP的解释是:Each class should have one and only one reason to change.一个类只能因为一个因素而改变。
SRP说的其实是类设计时的职责划分和粒度问题。没个类都是因为具有一定的职责才会存在,但是一个类也应该分担过多的职责,每一个职责的变化都会引起类的变化。这就好比一个人身兼数职,这些职责可能互不相干。但是一旦有一个职责发生变化,他就必须重新安排自己的工作。类也一样。过多互不相关(或现关性不强)的职责集中在一个类中就会造成高耦合性,代码僵化。因此,应该把不同的职责分开到不同的类中。
在OOD中,一个最典型最常用的符合SRP的设计就是分层结构:数据层专门负责数据持久化,处理和数据库(或其他数据源)的交互;业务逻辑层专门负责业务运作逻辑;表现层处理用户界面。一个层的变化不会对其他层造成影响,比如修改数据源只需要对数据层进行改动,其他两层完全不必知道。这是一个理想的结果。不过,实际情况下更多的是尽可能降低这种影响,而不能完全消除。
举个例子,我们在设计一个电子商务系统的时候,通常需要计算订单总价。订单总价的计算通常包含计算货物总价和运费两部分。成熟的设计中,Sale类用来计算货物总价,不应该再拿来计算运费。因为对于不同的产品、店铺、运送方式,运费的计算规则可能完全不同。
早期脚步语言编写的Web应用对SRP的破坏比比皆是:一个htm的页面呈现一个form,用户提交之后交给一个脚步程序去处理(比如asp/jsp)。这个脚本处理一切的东西:获取表单数据、进行必要的检查和处理、连接数据库进行存储,最后向用户进行反馈!一旦表单有变化,或者存储的数据库有修改,怎么办?每一个细节的变化都要修改这个脚本。因此,这根本就不是符合SRP的,甚至不能称之为OO的。
不过,我也见过另一个极端,整个程序非常简单,只有不到10个操作,而程序结构却异常复杂:分了4个层次,上百个大大小小的类。通常,一个类里只有一两个方法。这种设计方法也不能说是符合SRP的。SRP要求对每一个可能变化的职责都进行分开固然是正确的,但是这个变化一定要是现实中“真的可能”发生的。我们要专注于那些有较大可能发生变化的地方,而忽略那些不太可能变化或变化概率极小的细枝末节。比如说,你的数据访问逻辑是固定的,而中途变化数据库的可能也不大,因此,就没有必要把数据访问单独出来。一句话,不能为了“Everything is possible”,就把类结构设计的异常复杂。
所以,什么样的设计是符合SRP的,还需要根据实际的需要认真考虑,过犹不及,OOD也是需要中庸的。