单一职责原则(SRP)是设计模式五大原则之一。它规定一个类或一个对象只有一个发生变化的原因。如果需要开发的功能需求不是一次性的,且随着业务发展的不断变化而变化,那么当一个Class类负责超过两个及以上的职责时,就在需求的不断迭代、实现类持续扩展的情况下,就会出现难以维护、不好扩展、测试难度大和上线风险高等问题。
所谓的职责就是指类变化的原因,也就是业务需求。如果一个类有多余一个的原因被改变,那么这个类就有超过两个及以上的职责,这和单一职责相违背。单一职责要求的是一个类有且仅有一个改变类的原因。在实际开发过程中,这个要保持一个度,如果控制太严则可能会导致类数量泛滥;如果太松,则一个类可能会承担多种职责。
示例一:假设客户需求是需要开发一个视频网站用户分类的需求;
如果用户是游客(非会员):一般只可以观看480P的视频,并时刻提醒用户注册会员可以观看更高清的视频。
普通用户:一般可以观看720P的视频,但会有广告弹出。
VIP会员:用户可以观看1080P的视频,且可以屏蔽掉广告。
第一种实现方式是根据用户需求,直接设计一个程序,按用户需求进行if—else判断,实现客户需求,虽然能满足,但扩展性不好。开发完成后,我们会发现当调用方法时是所有的职责用户都使用一个方法实现,作为程序的调用入口。对于简单的或者几乎不需要迭代的功能,这样的实现比较简单也合适。但如果面对的是需要频繁迭代的业务需求,这样的代码结构就很难支撑系统的迭代。每一次新的需求实现可能都会影响到原来的其他的逻辑,这会给整个服务带来不可控的风险。如新增加一类用户时,就需要修改原来的代码,这样风险较高。采用单一职责模式可以提供系统的扩展性,如果新增用户类型,只需要写新的扩展类即可,其UML图如下。
实例二:开发一个电话程序
电话包括三个功能,电话的接通、通话、挂断三个功能。包含了两个职责一个是协议的管理一个是传送数据的管理,像电话的接通和挂断都需要涉及到协议,这里面包括电信的协议、联通的协议等;数据的传送包括传送语音信号,如果需要用电话线上网就涉及到数据信号的传送。协议或者数据类型的改变都会涉及电话类的改变,因此根据单一职责原则,这里需要定义两个类来承接电话对象。在开发过程中,由于电话有两个职责,这里我们定义两个接口做职责的拆分,接口一表示协议、接口二表示数据,每一个接口定义一个职责。但数据的传送是需要依赖协议的,因为只有协议建立之后才能进行数据传送。UML类图如下所示
在这里,我们可以发现,一个电话需要组装ConnectionManager和DataTransfer对象,这三个对象都具有相同的生命周期,耦合性非常高。类型这种场景如果方法不多,我们可以直接继承接口来实现。由于两个接口总共才三个方法,我们这里可以直接将phone继承IconnectionManager和IDataTransfer接口,实现接口的方法,这将可以有效的降低类的数量。修改后的UML图如下所示
在实际项目开发过程中,单一职责很难真正做到,也没必要一定要严格遵循单一职责。但我们一定要做到接口的职责单一,类的变化原因尽量设计成只有一原因引起的即可。