单一职责原则

Posted by zhangxiaojian on October 12, 2014

Single Responsibility Principle

There should never be more than one reason for a class to change

一个类只负责一个功能领域中的相应职责。或者说:就一个类而言,只有一个引起它变化的原因。

设计模式之禅中的例子:

假设有一个接口实现打电话的功能:

iphone

书中分析Iphone接口不是单一职责的,连接是一个职责,涉及连接协议的管理,聊天的时候又是一个职责,涉及信号的传输与解析。因此将接口拆开,实现类也拆开,然后组合在一起,变成了这样:

SR2

这样连接之后,给一个连接完成的句柄就可以随便聊了。因为Phone包含了链接和聊天两个功能,而且如果一个Phone对象被销毁之后,所建立的链接和数据传输对象当然也随之飘散,是强耦合的组合关系。为了消除这种强耦合,对实现类的“单一职责”做了妥协。

SR3

所有的实现功能都由Phone2一个类实现。书中阐述因为是“接口编程“的缘故,客户端在调用的时候都是调接口,真正的略显臃肿的实现类被隐藏了起来。

首先单一职责原则的职责划分是一门艺术,需要实际情况多方考虑定夺。上述例子明显阐述的是接口隔离原则。虽然体现的是单一职责的精神。但是其中关于实现类高耦合和单一职责的折中选择,在实际中,还是值得学习。

再看设计模式的艺术中一个例子:

有一个类需要完成从数据库读取信息然后会成图形显示出来:

SR4

其中包括了连接,查找,根据查找结果分析,显示几步。单一职责原则要求每一个类只有一个引起它变化的原因。这里的变化,我想指的是修改其中内容。比如getConnection方法已经写好,但是后期需要改变连接的策略,就需要对getConnection的代码进行修改。同样的,如果业务变化要求修改找部分Customer,比如VIP用户。或者要求从原来显示柱状图变成显示饼图。都要到这个类里面去修修改改。那引起变化的原因又指的是什么呢?原因是复杂的东西,因为它涉及到业务逻辑等等方面,This is something hard to say。这个例子中重构之后变成这样:

SR5

将变化的原因分为三种:数据库连接变化,数据处理变化,数据显示变化。如果业务逻辑上哪里有变更,就去哪个类改。这很符合常理,也易于理解。

但是想想比较极端的情况,因为柱状图和饼图的切换需要修改或者添加类中的方法,这算是引起类变化的原因,如果显示图的情况因为业务需求也要改,比如要一个屏幕显示四个图,田字型。那么这也是一个引起类变化的原因。看来有两个原因引起了同一个类的变化。是不是不符合单一职责原则了?要继续拆分和重构?of course not !

拆分也有一个限度,只要易于我们理解和维护就好。昨天在微博上看到Fowler一句话:

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. —— Martin Fowler

就是写的代码电脑都能运行,关键是人要看的舒服,维护着舒服。以此来确定职责的粒度吧。