如何确定一个班级是否符合单一责任原则?


34

单一责任原则基于高度凝聚力原则。两者之间的区别在于,具有高度凝聚力的班级具有一系列密切相关的职责,而遵守SRP的班级仅具有一个职责。

但是,我们如何确定某个特定类别是否具有一组职责并因此具有高度凝聚力,或者它是否仅具有一项职责并因此遵守SRP?换句话说,是不是有些主观,因为有些人可能认为一类非常细化(因此相信该类遵循SRP),而另一些人可能认为它不够细密?



Answers:


21

为什么是的,这是非常主观的,并且它是程序员参与的许多激烈的,红脸辩论的主题。

确实没有一个答案,而且随着软件变得越来越复杂,答案可能会改变。曾经是一个定义明确的任务,最终可能会变成多个定义不明确的任务。这总是很麻烦。您如何选择将程序划分为任务的正确方法?

关于我唯一可以提供的建议是:运用您(和您的同事)的最佳判断。请记住,如果您尽快发现错误,通常可以纠正这些错误。


我希望计算机科学更像是实际科学。主体性在现实科学中没有地位。尽管SOLID原则本身很好,但是需要对其进行迭代以最小化主观性和最大化客观性。这还没有发生,这使我质疑他们在现实世界中的合法性。
DarkNeuron

13

最早提出SRPSOLID原理的鲍勃·马丁(鲍勃叔叔)对此表示了看法(我是在措辞上,不记得实际的话):

一堂课只有一个改变的理由

如果有多个原因,则表明它不遵守SRP。


14
那只是在重复定义,但实际上坚持srp还是很主观的。
安迪

7

我可以给你一些经验法则。

  • 给班级命名有多容易?如果一个班级很难命名,那么它可能做得太多。
  • 该类有几种公共方法?7 +/- 2是一个很好的经验法则。如果该类的内容更多,则应考虑将其分为几个类。
  • 是否在不同的上下文中使用了凝聚力强的公共方法?
  • 有多少个私有方法或数据成员?如果该类具有复杂的内部结构,则您可能应该对其进行重构,以便将内部结构打包到单独的较小类中。
  • 最简单的经验法则:班级有多大?如果您有一个C ++头文件,其中包含一个单独的类,并且该类的长度超过了几百行,则应该将其拆分。

2
关于第二点,请参见uxmyths.com/post/931925744/…–
卡梅伦·马丁

7
强烈反对7 +/- 2-单责任原则是关于语义衔接,而不是关于任意数字。
JacquesB 2015年

1
经验法则不需要独立的科学证据。现代科学方法已有数百年历史,建筑和工程学已有数千年历史。公共方法的经验法则是“几个”,而参数个数不是“几个”。在其他新闻中,即使某些儿童绘画显示出其他方面的意义,人们的手臂也不会从他们的脑海中浮现出来。
abuzittin gillifirca

@CameronMartin根据您的设置,您可能无法使用或不易于使用类的界面来阅读。搜索UI几乎与编写代码相同–如果我必须每分钟查阅一次文档,至少我将完成任何实际工作所需的时间加倍。
清晰的

6

单一责任原则指出,每个软件模块都只有一个更改理由。在鲍勃叔叔最近的一篇文章中,他解释了“改变的理由”,

但是,在考虑这一原则时,请记住,改变的原因是人。是要求更改的人。而且,您不希望通过将许多不同人出于不同原因而关心的代码混合在一起来混淆这些人或您自己。

他用一个例子在这里进一步解释了这个概念。


那是由他本人写的一篇很棒的文章。
MrDustpan '16

4

要回答这个问题,请退后一步,考虑一下单一责任原则的意图。为什么首先将其推荐为设计原则?

该原理的目的是“分隔”代码库,因此与单个“职责”相关的代码被隔离在单个单元中。这使查找和理解代码变得更加容易,更重要的是,这意味着对“责任”的更改将只影响单个代码单元。

您绝对不希望在系统中发生的情况是,当一个很小的机会导致代码中其他显然无关的部分失败或改变行为时。SRP有助于隔离错误和更改。

那么,这是什么“责任”呢?可以想象,它可以独立于其他更改而更改。假设您有一个程序可以将一些设置保存到XML配置文件中,并且可以从文件中读回它们。这是单一职责,还是“加载”和“保存”两个不同的职责?对文件格式或结构的任何类型的更改都需要更改加载和保存逻辑。因此,这是一种责任,应该由一个班级来代表。现在考虑一个可以导出CVS,Excel和XML格式的数据的应用程序。在这种情况下,很容易想到一种格式可以更改而不会影响另一种格式。如果您决定以CVS格式更改定界符,则它不应影响Excel输出。


2

OO表示,类是功能的数据分组。这个定义为主观解释留下了很大的空间。

我们确实知道应该清晰,轻松地定义类。但是,为了定义这样的类,我们必须清楚地了解类如何适合整体设计。如果没有瀑布类型的要求,这很反常,那就是反模式……这很难实现。

我们可以使用在大多数情况下都可以使用的架构来实现类设计,例如MVC。在MVC应用程序中,我们仅假定具有数据,用户界面以及两者之间进行通信的要求。

使用基本架构,可以更轻松地确定单个职责规则被破坏的情况。EG将用户控件的实例传递给模态。


1

只是为了讨论起见,我将从JUCE 出一个名为AudioSampleBuffer的类。现在,该类可以保存一小段音频(或相当长的一小段音频)。它知道通道数量,样本数量(每个通道)似乎是致力于32位IEEE浮点数的,而不是具有可变的数字表示形式或字长(但这对我来说不是问题)。有一些成员函数可让您获取numChannels或numSamples以及指向任何特定通道的指针。您可以使AudioSampleBuffer更长或更短。我假设前者对缓冲区进行零填充,而后者则被截断。

此类的一些私有成员用于在JUCE使用的特殊堆中分配空间。

但这就是AudioSampleBuffer所缺少的(我已经与Jules进行过多次讨论):名为的成员SampleRate怎么会错过呢?

AudioSampleBuffer需要履行的唯一职责是充分代表人们听到其样本所代表的物理音频。当您从读取声音文件的内容或流中输入AudioSampleBuffer时,必须获取一个附加参数,并将其与AudioSampleBuffer一起传递给需要知道采样率的处理方法(例如,它是一个滤波器),或者最终,找到了一种方法来播放缓冲区(或将其传输到其他地方)。随你。

但是,您要做的就是继续传递此SampleRate,它是AudioSampleBuffer中特定音频固有的,并传递到任何地方。我看过将常量 44100.0f传递给函数的代码,因为程序员似乎不知道该怎么做。

这是一个未能履行其单一责任的例子。


1

根据您所说的,可以采取一种具体的方法-高内聚力导致您可以衡量内聚力的单一责任。最大内聚类具有所有方法中使用的所有字段。虽然最大的凝聚力类并非总是可能的,也不希望有最好的实现。有了这个类的设计目标,就很容易推断出您的类不能有很多方法或字段(有人说最多7个)。

另一种方法是从OOP的纯粹基础开始-在真实对象之后建模。它更容易看到真实对象的责任。但是,如果实际对象太复杂,则将其分解为多个包含对象,每个对象都有自己的责任。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.