设计原则-单一原则

定义:不要存在多于一个导致类变更的原因。意思就是不要让导致一个类变更的原因有多个,即一个类只负责一项职责。
打个比方: 有一个类A有两个不同的职责:职责一职责二,然后职责一变动了需求,然后去修改A类,改完之后发现职责一功能实现了,但是因为改动了A类导致职责二功能变了,或者说职责二功能故障了。
解决方法: 分别建立类A完成职责一、类B完成职责二
SRP(Single Responsibility Principle):There should never be more than one reason for a class to change.
我们写一个接口 电话类图

设计原则-单一原则_第1张图片
电话过程代码

public interface IPhone {
     //拨通电话
     public void dial(String phoneNumber);
     //通话
     public void chat(Object o);
     //通话完毕,挂电话
     public void hangup();
}

大部分的人都会说这个没有问题呀,以前我就是这么做的呀,某某书上也是这么写的呀,还有什么什么的源码也是这么写的!是的,这个接口接近于完美,看清楚了,是“接近”!单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情,看看上面的接口只负责一件事情吗?是只有一个原因引起变化吗?好像不是!

IPhone这个接口可不是只有一个职责,它包含了两个职责:一个是协议管理,一个是数据传送。dial()和hangup()两个方法实现的是协议管理,分别负责拨号接通和挂机;chat()实现的是数据的传送,把我们说的话转换成模拟信号或数字信号传递到对方,然后再把对方传递过来的信号还原成我们听得懂的语言。我们可以这样考虑这个问题,协议接通的变化会引起这个接口或实现类的变化吗?会的!那数据传送(想想看,电话不仅仅可以通话,还可以上网)的变化会引起这个接口或实现类的变化吗?会的!那就很简单了,这里有两个原因都引起了类的变化。这两个职责会相互影响吗?电话拨号,我只要能接通就成,甭管是电信的还是网通的协议;电话连接后还关心传递的是什么数据吗?通过这样的分析,我们发现类图上的IPhone接口包含了两个职责,而且这两个职责的变化不相互影响,那就考虑拆分成两个接口,其类图如图
职责分明的类图:
设计原则-单一原则_第2张图片
简洁清晰、职责分明的电话类图
设计原则-单一原则_第3张图片
这个类图看上去有点复杂了,完全满足了单一职责原则的要求,每个接口职责分明,结构清晰,但是我相信你在设计的时候肯定不会采用这种方式,一个手机类要把ConnectionManager和DataTransfer组合在一块才能使用。组合是一种强耦合关系,你和我都有共同的生命期,这样的强耦合关系还不如使用接口实现的方式呢,而且还增加了类的复杂性,多了两个类。经过这样的思考后,我们再修改一下类图,如图1-6所示。

这样的设计才是完美的,一个类实现了两个接口,把两个职责融合在一个类中。你会觉得这个Phone有两个原因引起变化了呀,是的,但是别忘记了我们是面向接口编程,我们对外公布的是接口而不是实现类。而且,如果真要实现类的单一职责,这个就必须使用上面的组合模式了,这会引起类间耦合过重、类的数量增加等问题,人为地增加了设计的复杂性。

通过上面的例子,我们来总结一下单一职责原则有什么好处:

● 类的复杂性降低,实现什么职责都有清晰明确的定义;

● 可读性提高,复杂性降低,那当然可读性提高了;

● 可维护性提高,可读性提高,那当然更容易维护了;

● 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

看过电话这个例子后,是不是想反思一下了,我以前的设计是不是有点问题了?不,不是的,不要怀疑自己的技术能力,单一职责原则最难划分的就是职责。一个职责一个接口,但问题是“职责”没有一个量化的标准,一个类到底要负责那些职责?这些职责该怎么细化?细化后是否都要有一个接口或类?这些都需要从实际的项目去考虑,从功能上来说,定义一个IPhone接口也没有错,实现了电话的功能,而且设计还很简单,仅仅一个接口一个实现类,实际的项目我想大家都会这么设计。项目要考虑可变因素和不可变因素,以及相关的收益成本比率,因此设计一个IPhone接口也可能是没有错的。但是,如果纯从“学究”理论上分析就有问题了,有两个可以变化的原因放到了一个接口中,这就为以后的变化带来了风险。如果以后模拟电话升级到数字电话,我们提供的接口IPhone是不是要修改了?接口修改对其他的Invoker类是不是有很大影响?

注意 单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

你可能感兴趣的:(设计原则-单一原则)