1.1 概述
单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出的。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change。该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:
♞ 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
♞ 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。
1.2 优点
① 提高类的可维护性和可读写性:一个类的职责少了,复杂度降低了,代码就少了,可读性也就好了,可维护性自然就高了。
② 提高系统的可维护性:系统是由类组成的,每个类的可维护性高,相对来讲整个系统的可维护性就高。
③ 降低变更的风险:一个类的职责越多,变更的可能性就更大,变更带来的风险也就越大,
1.3 案例
只要写过程序肯定接触过用户管理,用户有众多的信息和行为需要维护,我们将其写到一个接口中,如以下类图所示。虽然都是用户管理,但是用户的属性和行为却没有分开,等于就是大杂烩一锅端了,一个出现为问题可能会影响整个用户管理。这肯定是一个非常恶心的行为,如果我接手这种代码肯定立马辞职回家种地。
上面这个接口简直是个弟中弟,一般来说我们需要把属性和行为分别抽取为一个单独的接口,UserInfoInter 接口负责用户的属性信息,UserOperInter 接口负责用户操作行为,然后使用 UserInter 接口继承这两个接口。此时产生的 UserImpl 对象就可以在修改该属性是当作 UserInfoInter 使用,操作属性时当作 UserOperInter 使用。
上面这种方式看似满足要求,但是我们最初将将一个接口拆为两个接口的意义呢?在开发中我们会使用不同的类或接口来完成不同的功能,这里就可以分为 UserInfoInter 接口和 UserInfoImpl 实现类与 UserOperInter 接口和 UserOperImpl 实现类分别来完成属性和行为。以上我们将一个接口拆分为两个接口的行为就要体现了单一职责原则。
咱们再来强行刚一波,UserInfoInter 接口不是单一职责啊,有 set 也有 get。单一职责原则最难划分的就是职责,一个职责一个接口,但是问题是“职责”是一个没有量化的标准,一个类到底要负责那些职责?这些职责怎么细化?细化后是否都要有一个接口或类?这个都是需要从实际的项目区考虑的。
单一职责使用于接口、类,同时也使用方法,什么意思呢?一个方法尽可能做一件事情,比如一个方法既可以修改用户密码,又可以修改用户名,这个方法的颗粒度很粗。我们尽量让一个方法只做一件事,修改密码交给修改密码的方法,修改用户名的交给修改用户名的方法,不要使用一个修改一个用户信息的方法搞定所有。这样在方法上保证了单一职责原则。
对于接口,我们在设计的时候一定要做到单一,但是对于实现类就需要多方面考虑了,生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦;而且过分的细分类的职责也为人为的制造系统的负责性,本来一个类可以实现的行为非要拆成两个,然后使用聚合或组合的方式再耦合在一起,这个是人为制造了系统的复杂性,原则是死的,人是活的。