单一责任原则是否适用于职能?


17

根据Robert C. Martin所说,SRP指出:

改变班级的理由不应该只有一个以上。

但是,在他的《清洁代码》第3章:函数中,他显示了以下代码块:

    public Money calculatePay(Employee e) throws InvalidEmployeeType {
        switch (e.type) {
            case COMMISSIONED:
                return calculateCommissionedPay(e);
            case HOURLY:
                return calculateHourlyPay(e);
            case SALARIED:
                return calculateSalariedPay(e);
            default:
                throw new InvalidEmployeeType(e.type);
        }
    }

然后指出:

此功能有几个问题。首先,它很大,并且当添加新的雇员类型时,它会增长。其次,它显然做的不止一件事。第三,它违反了“单一责任原则”(SRP),因为有多个原因使其改变。[强调我的]

首先,我认为SRP是为类定义的,但事实证明它也适用于函数。其次,该功能有多个原因需要改变吗?我只能看到它随着Employee的变化而改变。


5
这似乎是多态性的教科书案例。
wchargin

这是一个非常有趣的话题。您是否有可能为该问题添加以下解决方案?我很怀疑有人在每个员工类别中添加一个calculatePay功能,但是这很不好,因为现在每个员工类别都可以更改为:1。付款计算。2.添加更多的属性的类等。
STAV阿尔菲

Answers:


13

单一责任原则经常遗漏的一个细节是,“变更原因”是按用例参与者分组的(您可以在此处看到完整的解释)。

因此,在您的示例中,calculatePay每当需要新类型的Employees时,都需要更改该方法。由于一种类型的员工可能与另一种类型的员工无关,因此如果将它们放在一起,则将违反该原则,因为更改会影响系统中的不同用户组(或用例参与者)。

现在,关于该原理是否适用于函数:即使仅在一种方法中发生违规,您仍出于多种原因而更改类,因此仍然违反SRP。


1
我试图观看链接的youtube视频,但是播放10分钟后,却没有提到用例参与者进行分组,所以我放弃了。最初的6分钟都是毫无意义的熵。如果您在视频中提供了一个他开始讨论此问题的位置,那将很有帮助。
Michael Shaw

@MichaelShaw尝试从10:40开始观看。鲍伯叔叔提到代码将“由于不同的人而因不同的原因而改变”。我认为这可能是MichelHenrich试图指出的。
恩里克

看完了整个50分钟的youtube视频,其中大部分与应该澄清的内容无关。我注意到在16:00,他已经离开了这个话题,他再也没有回到这个话题。“解释”从本质上可以归结为:SRP中的“责任”并不意味着,它意味着“变化的不同原因”,这实际上意味着“应不同人的请求而变化”,这实际上意味着“在不同人的要求下进行变化”。要求人们扮演不同的角色”。“解释”没有任何说明,它用一个模糊的词替换了另一个。
Michael Shaw 2015年

2
@MichaelShaw就像书中的引用一样,如果您需要引入不同的员工类型,则必须更改Employee类。不同的角色可能负责支付不同的雇员类型,因此在这种情况下,必须更改Employee类以使用多个角色,因此违反了SRP。
MichelHenrich'3

1
@MichaelShaw是的,您是对的-SRP确实取决于组织的组织方式。这就是为什么我在所有评论中加上“可能”或“可能”的原因:)。但是,即使在这些情况下,虽然代码可能不会违反SRP,但它确实确实会违反OCP。
MichelHenrich'3

3

在第176页的第12章:出现中,在标题为“ 最小类和方法 ” 的部分中,该书通过指出以下内容提供了一些更正:

为了使我们的类和方法变小,我们可能会创建太多微小的类和方法。因此,此规则表明我们还应保持函数和类计数较低

高等级的方法有时是无意义的教条主义的结果​​。

显然,他是在遵循SRP来打破教条主义,以打破像calculatePay()上面这样的完美无辜的小方法。


3

当马丁先生将SRP应用于某个功能时,他隐含地扩展了SRP的定义。由于SRP是通用原则的OO特定用语,并且由于将其应用于函数是个好主意,所以我认为这没有问题(尽管如果他明确地将其包含在函数库中可能会很好。定义)。

我看不到有一个以上理由来进行更改,并且我也不认为从“责任”或“更改原因”的角度考虑SRP是有帮助的。SRP本质上是指软件实体(功能,类等)应该做一件事情并且做得很好。

如果您看一看我的定义,它与SRP的惯用措词一样清晰。SRP的常规定义所存在的问题不是它们过于模糊,而是它们试图对本质上模糊的事物过于具体。

如果您看做什么的话calculatePay,显然是在做一件事情:基于类型的调度。由于Java具有内置的基于类型的分派方式,calculatePay因此不雅致且非惯用语,因此应将其重写,但出于所陈述的原因,不能重写。


-2

您说对了@Enrique。无论是函数还是类的方法,SRP都意味着在该代码块中,您只能做一件事。

“更改原因”语句有时会引起误解,但是如果您更改calculateSalariedPaycalculateHourlyPay或者Employee.type您的枚举必须更改此方法。

在您的示例函数中:

  • 检查员工的类型
  • 调用另一个根据类型计算货币的函数

我认为这不是直接违反SRP,因为如果您想到Employee并且方法已经存在,则切换案例和调用不能写得更短。无论如何,这显然是违反开放式原则(OCP)的,因为如果添加员工类型,则必须附加'case'语句,因此这是一个不好的实现:重构它。

我们不知道应该如何计算“货币”,但是最简单的方法是拥有Employee接口和一些带有getMoney方法的具体实现。在那种情况下,整个功能是不必要的。

如果计算起来更复杂,则可以使用访问者模式,该模式也不是100%SRP,但是它比开关情况要多的OCP。


2
不知道如何列出该功能可以完成的两件事,但是说这不是违反SRP的。
JeffO 2015年

@JeffO:这不是两件事,而是一件事的两部分:根据类型调用适当的函数。
Michael Shaw
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.