高凝聚力是单一职责原则的同义词吗?如果不是,它们有何不同?
4 回答
你问:如果不是,它们有什么不同?
单一职责原则
扔掉任何你认为你猜到这个原则意味着什么的东西。
Robert C. Martin 正式将此原则定义为:
一个班级应该只有一个改变的理由
大多数人定义 SRP 是不正确的。SRP 通常被误解为:
“这个Employee
类不应该UpdateDemographics()
和SendMessage()
,那算作两个责任……放到SendMessage()
一个Message类中!!”
^^ 错了
vv 对
罗伯特·C·马丁 说
“责任不是'代码所做的事情'”。(国家数据中心 2012)
Robert C. Martin 将 SRP 定义为:
“任何模块都应该只对一个人负责——[角色]。” (国家数据中心 2012)
当利益相关者要求更改视图中的数据排序时,管理层不应惊慌失措并担心算法会崩溃。因为在视图上处理排序的模块只对涉众负责,而处理总计算法的模块只对业务分析师负责。 因此,当业务分析师要求对算法进行更改时,我们不应该担心视图会发生变化。
因此,一个模块(单数)应该只因为一个原因而改变:这个模块所服务的单人角色请求改变。
以此作为您定义 SRP 的新基础,您现在可以应用您之前认为的SRP,并使定义更加细化。没有人说要把你所有的前端代码放在一个模块里,把你所有的后端代码放在一个模块里。
高凝聚力
想象一下你有一个方法decimal CalculatePayFor(Employee)
和另一个方法void Pay(Employee)
这些属于一起吗?可能存在执行各种计算的服务,并且可能存在仅包装人力资源支付 SOAP 的服务。也许Pay(Employee)
呼入CalculatePayFor(Employee)
,但仅仅因为他们有这个词Pay
并不意味着他们属于一起!
我如何创造凝聚力?- 你没有。凝聚力是你观察到的东西。你要做的是不要拆散属于一起的东西。
可以为您想要的每个公共方法创建一个类。现在每个类都有一个公共方法,一切都是定义明确的混乱。您有命名的类PayrollClass1
,PayrollClass2
因为一个以一种方式进行计算,而另一个以两种方式进行计算。
一些语言甚至受益于完全没有类,并且方法可以免费运行。没有方法分组,方法只是您可以随时调用的方法。它们几乎都是静态的。
但是,您可以观察到这一点CalculatePayFor(Employee)
,并且Pay(Employee)
实际上是非常受约束的。他们就像一对已婚夫妇,他们在一起看起来很棒。当方法明显属于一起时,您不想将它们分开。观察它们的自然状态并建立野生动物保护区。这就是保持高凝聚力。你不创造它,你观察它。
这真正有帮助的是正确的代码复制。例如,PayrollService.CalculatePayFor(Employee)
具有与ReportService.CalculatePayFor(Employee)
. 这很糟糕吗?当然不是。如果高级管理层要求更改员工薪酬的计算以进行报告,那与人力资源部门要求您更改实际付款方式的税收计算的责任不同。
“等等,他是不是把 SRP 和凝聚力混为一谈了?” 不,但我很高兴你认出了这个混淆。如果 ReportService 进入 PayrollService' 类并使用它的方法怎么办?然后当它为了合法的支付目的而改变时,报告都改变了……但管理层不希望这样!!!因此,由于 Cohesion 强制方法保留在自己的类中,而 SRP 强制模块保留在应用程序中,因此 ReportService 被迫从 PayrollService 类中复制/粘贴方法。现在它们可以相互独立地改变。
“但如果这不是你想要的呢?” 好吧,代码中有很多地方都排除了重复。但最常见的情况是算法会坚持自己,并且独立于依赖关系而变化。即使这意味着重复。它只取决于需要什么。但是关注点分离、单一职责、凝聚力和 DRY(不要重复自己)都是独立的想法。
旁注:干燥并不意味着永远不会重复。正如我所提到的:很多时候你可以有重复的代码,因为业务规则在不同的关注点之间是相似的,并且有不同的理由来改变。
不是一回事。
您可以拥有一个高度内聚的类,而不仅仅是一个单一的职责。
用罗伯特·马丁自己的话说,
如果您考虑 [SRP],您会意识到这只是定义内聚和耦合的另一种方式。我们希望增加因相同原因而发生变化的事物之间的内聚,并希望减少因不同原因而发生变化的事物之间的耦合。
另请参阅:单一职责原则与关注点分离之间的区别。