单一职责原则(SRP)解读

单一职责原则的定义


鲍勃大叔( Robert C. Martin)这样定义单一职责原则:

A class should have only one reason to change

一个类应该只有一个发生变化的原因


定义非常简洁。然而,使用这一原则却不像看起来那么容易。


这样理解SRP对吗?


记得前一段时间读过一篇批评SRP的文章,大意是说SRP是一个错误的想法,并不能帮助程序员理解高内聚(high cohesion)的概念,并举了一个例子来证明。例子是这样的:

class AwsOcket {

  boolean exists() { /* ... */ }

  void read(final OutputStream output) { /* ... */ }

  void write(final InputStream input) { /* ... */ }

}


显然,这个类有三个职责:

  1. 检查AWS S3中对象是否存在; 

  2. 把对象内容写入输出流; 

  3. 把输入流的对象写到AWS S3。


这显然违反了SRP原则。根据SRP来重构一下,拆分出三个类:ExistenceChecker、ContentReader和ContentWriter。重构前,为了读取内容并将其打印到控制台,代码大概这样:

if (ocket.exists()) {

     ocket.read(System.out);

}


重构后,代码这样:

if (new ExistenceChecker(ocket.aws()).exists()) {

  new ContentReader(ocket.aws()).read(System.out);

}


显然,重构后反而变糟糕了。对SRP的批评看起来似乎没毛病。鲍勃大叔错了吗?没有。是文章作者曲解了SRP。


如何理解SRP?


首先,鲍勃大叔是理解高内聚的。他在《Agile Software Development, Principles, Patterns, and Practices》中SRP一章开篇就说:


The principle was described in the work of Tom Demarco and Meilir Page-Jones. They called it cohesion … In this chapter, we modify that meaning bit and relate cohesion to the forces that cause a module, or a class, to change.

Tom Demarco  Meilir Page-Jones曾经在他们的著作里描述这个原则。他们称之为内聚性……本章中,我们稍微改一下它的含义,把内聚性和引起一个模块或者类改变的作用力联系起来。


显然,鲍勃大叔不是抛弃内聚性,而是换个角度看问题。他在书中进一步说:

In the context of SRP, we define a responsibility to be a reason for change.

SRP上下文中,我们把职责定义为变化的原因。这样,SRP的定义就等同于“一个类应该只有一个职责”,所以SRP叫单一职责原则。


如何理解职责就变成了理解SRP的关键。上面提到的文章显然把一个职责等同于一个功能了。这是不符合鲍勃大叔的本意。鲍勃大叔这样解释一个职责:

So a responsibility is a family of functions that serves one particular actor.

一个职责是一个服务于某个特定参与者的功能集


既然一个职责服务于一个特定的参与者,那么参与者就是职责变化之源:

An actor for a responsibility is the single source of change for that responsibility. 


考察一个职责,必然要考察参与者,从参与者的角度识别职责。


举个栗子


如鲍勃大叔书中所提,混合持久化逻辑和业务逻辑是常见的违反SRP的情况。例如:

单一职责原则(SRP)解读_第1张图片


对于Book而言,有两个截然不同的参与者:Reader和FilePersistence。Reader需要通过get方法获取书的标题和内容,Book通过FilePersistence存储自己。Reader和FilePersistence任何一个需求的变化都会带来Book的变化,即变化原因不唯一。正确的做法是把Save方法从Book中剥离出去:

单一职责原则(SRP)解读_第2张图片



总结


  • SRP看似简单,正确使用却难。

  • 单一职责不意味着一个类只有一个方法,而是一组高内聚的方法。

  • 从参与者角度去考虑职责会有很大帮助。


—————END—————


关注夫子程序社,更多精彩

单一职责原则(SRP)解读_第3张图片

你可能感兴趣的:(架构与设计)