MaWenge's Blog

设计模式----单一职责原则

设计原则

面向对象的编程有很多的设计模式,例如单例模式,观察者模式,装饰者模式等等,这些模式适用于不同的场景,但是这些设计模式又遵循几个相同的原则,可以说这些原则是所有设计模式的基础

  1. 单一职责原则
  2. 里氏替换原则
  3. 依赖倒置原则
  4. 接口隔离原则
  5. 迪米特法则
  6. 开闭原则

如果能够很好地理解这些原则,对以后学习具体的设计模式将会有很大的帮助,所以这里我也结合所看的书籍并结合自己的理解深入剖析每种原则的含义。如果发现有不准确的地方,欢迎留言交流,共同进步。

单一职责原则

单一职责的英文名称是Single Responsibility Principle,简称SRP。SRP原话解释如下:

There should never be more than one reason for a class to change
应该有且只有一个原因引起类的变更

其实单一职责这句话本身就已经说得很清楚了,就是一个类只负责一件事,不负责其他的事情。而能够引起这个类变化的原因也是只有这个类负责的事情。

这里举一个例子,也是书上的例子:
打电话需要四个过程,拨号、通话、回应、挂机,现在写一个接口如下

这三个方法都是电话的功能,把他们全都放在IPhone的接口中也是非常合理的。而且它就负责一件事,电话的事情,但只有一个原因引起变化吗?好像不是
IPhone这个接口其实有两个职责:一是协议管理,二是数据传送。dial() hangup()实现的是协议管理,分别负责拨号接通和挂机;chat()实现的是数据的传送,把模拟信号转换成数字信号等等。协议的变化和数据传送的变化都能够引起接口或实现类的变化。那么协议控制和数据传送之间相互依赖吗?电话拨号,我们只要接通就成,甭管是电信还是网通的协议;电话接通后我们也不关心传递的是什么数据。通过这样的分析我们发现类图上的IPhone接口包含了两个职责,而且这两个职责的变化不会相互影响,那就考虑拆分成两个接口

那么这么做到底能有什么好处呢,请看下面:

  1. 我们使用的是面向接口的编程,所以暴露给使用者的是两个接口类型IConnectionManager和IDataTransfer。使用者只能调用接口中声明的方法。面向接口编程实现了方法的隔离,使得使用者只需要使用方法即可,不需要知道具体实现,并且只能使用该接口的方法。所以在业务层面与协议有关的就是IConnectionManager,而与数据传输相关的就是IDataTransfer,到时候如果修改了接口里面的内容,业务调用层需要修改的内容范围也会减小(例如如果修改了数据传输IDataTransfer,那么业务层只需要修改IDataTransfer有关的内容,IConnectionManager相关的内容则完全不用修改,找起来也方便一点,如果没有拆分成两个接口,那么就要找到所有IPhone接口使用的地方一一检查)

  2. 这里的实现类Phone仍然实现了打电话有关的所有功能,只不过对于不同的使用情况通过接口类型暴露不同的方法给调用者。那么有人肯定要问为什么不定义两个实现类分别实现IConnectionManager和IDataManager,然后分别把两个实现类的对象传递给接口对象。这里IConnectionManager和IDataManager的实现方法如果要共享数据的话,那么让Phone类同时实现IConnectionManager和IDataManager接口,这样就可以很方便的在类内部维护共享数据以及状态,而通过接口对外暴露功能方法,类的结构也很清晰。还有一种就是Phone类组合IConnectionManager和IDataManager的实现类,但是组合是一种强耦合关系,而且还增加了类的复杂性,多了两个类,还不如直接让Phone类实现IConnectionManager和IDataManager接口。

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

上面的例子从功能上来说,定义一个IPhone接口来说也是没有错的,实现了电话的功能,而且设计还很简单,仅仅定义一个接口和一个实现类。但是确实是有两个可以变化的原因放到了一个接口里面,这就为以后的变化带来了风险。如果以后模拟电话升级到数字电话,我们提供的接口IPhone是不是要修改了?接口修改对其他的Invoker类是不是有很大的影响?

单一职责适用于接口、类,同时也适用于方法。一个方法尽量做一件事。我相信大多数人编程的时候突然发现接下来要写的一段代码以前好像写过,然后回去把那段代码又提取出来变成一个单独的方法。这就时前面设计方法的时候做了太多事情,粒度太大了,相信大多数人多方法的单一职责还是比较容易理解的。