引子
很多中央看过或听过 繁多责任准则,看了《The Single Responsibility Principle》这篇文章后,有了新的意识,翻译记录一下。原文中有些链接生效了,找了资源从新换了下。
翻译原文:The Single Responsibility Principle
- Origin
- My GitHub
注释
1972 年,David L. Parnas 发表了一篇优秀论文,题目是《On the Criteria To Be Used in Decomposing Systems into Modules》。这篇文章呈现在 12 月号的《Communications of the ACM》,第 15 卷,第 12 期。
在这篇论文中,Parnas 在一个简略的算法中,比拟了两种不同的逻辑合成和拆散策略。这篇论文读起来很乏味,强烈建议你钻研一下。他的局部论断如下:
咱们试图通过这些例子证实,依据流程图开始将零碎合成为模块简直总是不正确的。取而代之,咱们倡议从一系列艰难的设计决策或 可能扭转 的设计决策开始。而后,每个模块都设计成对其它模块暗藏这样的决策。
我在第二句加了重点强调。Parnas 的论断是,模块应该依据它们可能扭转的形式进行拆散,至多局部是这样。
两年后,Edsger Dijkstra 写了一篇题为《On the role of scientific thought》的优秀论文。在这篇文章中,他引入了一个术语:关注点的拆散(The Separation of Concerns)。
20 世纪 70 年代和 80 年代是软件体系结构准则的流行期间。结构化编程和设计(Structured Programming and Design)风行一时。在这段时间里,Larry Constantine 引入耦合(Coupling)和内聚(Cohesion)的概念,Tom DeMarco、Meilir Page-Jones 和其余许多人都对其进行了扩大。
20 世纪 90 年代末,我试图将这些概念整合成一个准则,我称之为:繁多责任准则(The Single Responsibility Principle)。(我有一种含糊的感觉,我从 Bertrand Meyer 那里偷了这个准则的名字,但我还没能证实这一点。)
繁多责任准则(SRP)规定,每个软件模块都应该有且只有一个扭转的理由。这听起来不错,仿佛合乎 Parnas 的理念。然而,它回避了一个问题:什么定义了扭转的理由?
一些人想晓得一个 bug 修复是否能够作为一个扭转的理由,另一些人认为重构是否是扭转的理由。这些问题能够通过指出“扭转的理由””和“责任”之间的耦合来答复。
当然,这些代码对 bug 修复或重构并不负责。这些事件是程序员的责任,而不是程序的责任。但如果是这样的话,这个程序负责什么?或者,一个更好的问题是:这个程序对 谁负责?更好的是:程序的设计必须 回应谁?
设想一个典型的商业组织,高层有一位 CEO(首席执行官),向 CEO 报告的 C 级高管有:CFO(首席财务官)、COO(首席运营官)和 CTO(首席技术官)等。CFO 负责管制公司的财务。COO 负责管理公司的经营。CTO 负责公司外部的技术基础设施和开发。
当初思考上面的一段 Java 代码:
public class Employee {public Money calculatePay();
public void save();
public String reportHours();}
calculatePay
办法实现了一些算法,这些算法依据员工的合同、职位、工作工夫等因素,来确定该员工应该失去多少报酬。save
办法将Employee
对象治理的数据存储到企业数据库中。reportHours
办法返回一个字符串,该字符串会增加到报表中,审计员应用该字符串来确保员工的工作工夫适合,并取得适合的报酬。
当初,向 CEO 报告的 C 级高管中,谁负责规定 calculatePay
办法的行为?如果这个办法的规定呈现严重错误,他们中的哪一个会被 CEO 辞退?显然,答案是 CFO。规定雇员的工资是一项财务责任。如果因为 CFO 组织中有人规定了严重错误的计算薪酬规定,所有员工一年的薪酬都是双倍的,那么 CFO 很可能会被辞退。
另一个 C 级高管负责规定 reportHours
办法返回字符串的格局和内容。这个高管治理审计员和审核员,这是经营部门的职责。因而,如果该报告呈现灾难性的谬误规定,COO 将被辞退。
最初,如果 save
办法呈现灾难性的谬误规定,那么哪些 C 级主管将被辞退应该不言而喻。如果企业数据库被如此可怕的谬误规定所毁坏,那么 CTO 很可能会被辞退。
因而,当 calculatePay
办法中的算法产生更改时,这些更改的要求将来自以 CFO 为首的组织,这是正当的。相似的,COO 的组织将要求更改 reportHours
办法,CTO 组织将要求更改 save
办法。
这就是 繁多责任准则 的关键所在。这个准则是对于人的。
在编写软件模块时,你心愿确保在申请变动时,这些变动只能来自单个人员,或者更确切地说,来自一个严密耦合群体,这个群体代表一个严格定义的业务性能。你心愿将你的模块,从整个组织的复杂性中隔离开来,并这样设计你的零碎,使每个模块只负责(响应)一个业务性能的需要。
为什么?因为咱们不想因为 CTO 的更改要求而导致辞退 COO。对于咱们的客户和经理来说,从他们的角度,发现一个程序的故障,与他们所要求的更改齐全无关,没有什么比这更让他们胆怯了。如果你更改 calculatePay
办法,并且无心中毁坏了 reportHours
办法,那么 COO 将开始要求你再也不要更改 calculatePay
办法了。
设想一下,你把车开到了一个修理工那里,为了修理一扇坏了的电动车窗。他第二天打电话通知你所有都修好了。当你提车时,你发现车窗运作失常,但车动员不起来。你不太可能回到那个机修工那里,因为他显然是个笨蛋。
这就是当客户和经理 没有要求 咱们扭转,咱们毁坏他们所关怀的事件时的感触。
这就是咱们不在 jsp 中应用 SQL 的起因。这就是咱们不在计算结果的模块中生成 HTML 的起因。这就是业务规定不应该晓得数据库模型的起因。这就是咱们 拆散关注点 的起因。
繁多责任准则 的另一个表白是:
将因同样起因而扭转的事件集中起来。将因不同起因而扭转的事件离开。
如果你思考一下,就会发现这只是定义内聚和耦合的另一种形式。咱们心愿减少因雷同起因而扭转的事物之间的内聚力,并缩小因不同起因而扭转的事物之间的耦合。
不管怎样,当你思考这个准则时,记住扭转的起因是 人。是 人要求扭转。不同人的关怀出于不同的理由,你并不想把这些代码混合在一起,混同那些人或者你本人。
参考资料
- The Single Responsibility Principle