在使用单一责任原则时,您如何确定“责任”应该是多么粗糙或细粒度?

时间:2010-03-16 15:31:56

标签: oop single-responsibility-principle solid-principles

在SRP中,“责任”通常被描述为“改变的理由”,因此每个类(或对象?)应该只有一个人应该去那里并改变它的原因。

但是,如果你把它变成极端细粒度,你可以说一个对象将两个数字加在一起是一个责任,也是一个可能的改变原因。因此,该对象不应包含其他逻辑,因为它会产生另一个改变的原因。

我很好奇是否有人有任何“范围界定”策略,单一责任原则稍微不那么客观?

6 个答案:

答案 0 :(得分:27)

它归结为你正在建模的背景。我已经做了一些广泛的写作并介绍了SOLID原则,我在单一责任的讨论中专门解决了你的问题。

以下内容首次出现在2010年1月/ 2月的“Code Magazine”杂志上,可在"S.O.L.I.D. Software Development, One Step at a Time"

在线获取。
  

单一责任原则   说一个班级应该有一个,和   只有一个,有理由改变。

     

这似乎与直觉相反   第一。说不是更容易   一个班级应该只有一个   理由存在?实际上,没有人   存在的理由很容易   采取了可能导致的极端   弊大于利。如果你接受它   那个极端和构建类   有一个理由存在,你可能会结束   每班只有一种方法。   这会导致大量的蔓延   即使是最简单的课程   进程,导致系统   难以理解和困难   改变。

     

一个班级应该有的原因   改变的一个原因,而不是一个   存在的理由,就是生意   你正在构建的上下文   系统。即使有两个概念   在逻辑上不同,业务   他们需要的背景可能   需要他们成为一个和   相同。决定什么时候的关键点   班级应该改变不是基于a   纯粹逻辑上的概念分离,   而是企业的看法   这个概念。什么时候做生意   感知和背景已经改变,   那么你有理由改变   类。要明白什么   一个班级应该承担的责任   有,你需要先了解   什么概念应该被封装   那个班级和你期望的地方   该概念的实施细节   改变。

     

考虑汽车中的发动机   例。你关心内心吗?   发动机的工作?你关心   你有一个特定的大小   活塞,凸轮轴,喷油器等?   或者,你只关心发动机   当你进入时,按预期运作   车?答案当然是   完全取决于上下文   你需要使用引擎。

     

如果你是一名工作的技师   汽车店,你可能在意   发动机的内部工作原理。你需要   要知道具体的模型,   各种零件尺寸等   发动机的规格。如果你   没有这些信息,   你很可能无法为引擎提供服务   适当。但是,如果你是一个   只有平均每天的人   需要从A点到A点的运输   B点,你可能不需要那个   信息水平。的概念   各个活塞,火花塞,   滑轮,皮带等,差不多   对你毫无意义。你只关心那个   你开的车有发动机   并且它正确执行。

     

引擎示例直接驱动   单一责任的核心   原理。驾驶的背景   汽车与维修发动机提供   什么应该是两个不同的概念   并且不应该是一个单一的概念 - a   改变的理由。在上下文中   为每个人提供发动机维修服务   部分需要分开。你需要   将它们编码为单个类并确保   他们都取决于他们的个人   规格。在上下文中   然而,驾驶汽车,发动机是一个   单一的概念,不需要   进一步细分。你会   可能有一个叫做的课程   在这种情况下,引擎。在任一情况下,   上下文确定了什么   适当分离   责任是。

答案 1 :(得分:4)

我没有看到执行像添加两个数字一样的任务。责任有不同的形状和大小,但它们当然应被视为比执行单一功能更大的东西。

为了更好地理解这一点,明确区分类负责的内容和方法的作用可能会有所帮助。一种方法应该“只做一件事”(例如,添加两个数字,但是对于大多数用途来说'+'是一种已经完成的方法),而一个类应该向其消费者提供一个明确的“责任”。它的责任在于比方法更高的水平。

像Repository这样的类具有明确且单一的责任。它有多种方法,如Save和Load,但明确的责任是为Person实体提供持久性支持。一个班级也可以协调和/或抽象依赖班级的责任,再次将其作为对其他消费类别的单一责任。

底线是,如果SRP的应用程序导致单方法类,其全部目的似乎只是将该方法的功能包装在类中,则SRP未正确应用。

答案 2 :(得分:3)

我倾向于考虑业务需求的“变化速度”而不是“改变的理由”。

问题确实是什么东西会一起改变 ,而不是它们是否会改变。

差异很微妙,但帮助我。让我们考虑wikipedia关于报告引擎的示例:

  • 如果报告的内容和模板同时发生变化的可能性很高,则可以成为一个组件,因为它们显然是相关的。 (它也可以是两个)

  • 但如果内容在没有模板的情况下更改的可能性很重要,那么必须是两个组件,因为它们不相关。 (拥有一个会很危险)

但我知道这是对SRP的个人解释。

另外,我喜欢的第二种技巧是:“用一句话描述你的课程”。它通常有助于我确定是否有明确的责任。

答案 3 :(得分:3)

我使用的一个简单的经验法则是:责任的级别或粒度应与所讨论的“实体”的级别或粒度相匹配。显然,方法的目的总是比类,服务或组件更精确。

评估责任水平的良好策略可以是使用适当的比喻。如果您可以将您正在做的事情与现实世界中存在的事物联系起来,那么它可以帮助您更好地了解您正在尝试解决的问题 - 包括能够确定适当的抽象和责任级别。

答案 4 :(得分:3)

@Derick bailey:很好的解释 一些补充:
SRP的应用是上下文基础是完全可以接受的。
问题仍然存在:是否有任何客观的方法来定义某个类是否违反了SRP?

一些设计上下文非常明显(比如Derick的汽车示例),但是其中一个类的行为必须定义的上下文仍然模糊多次。

对于这种情况,如果通过将模糊类行为分为不同的类来分析模糊类行为,然后测量由于分裂而产生的新行为和结构关系的影响,则可能会有所帮助。

一旦完成拆分,保持分离责任或将其合并为单一责任的理由立即变得明显。

我已经应用了这种方法,并且为我带来了良好的效果。

但我寻找“定义阶级责任的客观方式”的搜索仍在继续。

答案 5 :(得分:2)

当克里斯·尼古拉(Chris Nicola)上面说“一个班级应对其消费者提出一个明确的责任”时,我尊重并不同意

我认为SRP是关于在课堂上设计好的,而不是课堂上的客户。

对我而言,责任是什么并不是很清楚,证明是这个概念产生的问题的数量。

  

“改变的唯一理由”

  

“如果描述包含单词   “和”那么它需要分开“

引出了一个问题:限制在哪里?最后,任何有2种公共方法的班级都有2个改变的理由,不是吗?

对我来说,真正的SRP导致Facade模式,你有一个类只是简单地删除对其他类的调用

例如:

class Modem
  send()
  receive()

Refactors to ==>

class ModemSender
class ModelReceiver

+

class Modem
  send() -> ModemSender.send()
  receive()  -> ModemReceiver.receive()

意见很好