理解单一职责原则

11

我很困惑如何确定一个方法是否只负责一项职责,就像书中的以下代码一样。

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呢?但当我进一步理解该方法时,我得出了一个假设,即由于该方法的名称是calculatePay(Employee e),因此该方法唯一的职责是进行付款计算,正如方法名所示,但由于在方法内部有一个用于过滤Employee类型的switch语句,这种过滤现在是另一种职责,因此违反了SRP。我不知道我的理解是否正确。
2个回答

12
「...显然做了不止一件事。第三,它违反了单一职责原则(SRP),因为它有不止一个改变的原因。」
这就是答案所在。每次添加新的Employee.Type时,该方法都必须更改。此外,每个Employee.Type的计算也可能会发生变化。
更好的解决方案是为员工和每个员工衍生类创建一个抽象工厂,使其具有自己实现的CalculatePay。这样,当计算发生变化或添加新的Employee.Type时,只需要更改一个类即可。
以下是《代码整洁之道》中的另一段摘录,其中对此进行了更详细的解释 -
这个问题的解决方案(参见3-5清单)是将switch语句埋藏在抽象工厂的地下,永远不要让任何人看到它。 工厂将使用switch语句创建Employee派生类的适当实例,并且各种函数(例如calculatePay,isPayday和deliverPay)将通过Employee接口以多态方式分派。 我对switch语句的一般规则是:如果它们只出现一次,并用于创建多态对象,则可以容忍它们。

感谢您的输入,我想问一下此外,每个员工类型的计算方法也可能会改变。员工的计算方式如何影响该方法?由于每种类型只是调用一个方法来执行计算,我认为根据您的回答和书中给出的陈述,我可以假设我对原理的理解是正确的吗? - anathema
2
“单一职责原则”应该在类的范畴内考虑,而不是仅限于方法级别。这就是为什么我说如果任何计算发生变化,您需要更新这个类。如果您将此方法移动到Employee的派生类中,则只有该类需要更改,如果需要更改计算,则只需更改该类即可。 - Janus Pienaar
@anathema 如果我们想要根据不同的小时支付方式进行计算(比如说周末工作会影响薪资),那么我们可能需要改变calculateHourlyPay函数的签名(添加一个布尔标志或日期时间),因此calculatePay函数也需要被修改。 - Robert F.

6

我通常将SRP应用于类级别。这可以防止类变得过大并承担过多的角色。

我将'responsibilities'视为概念性的。因此,我会认为你的方法只有一个职责:计算薪水。

我尝试遵循Google的指南,并寻找以下警告信号,表明您正在偏离SRP:

  • 概括类的功能包含单词'and'。
  • 对于新团队成员或经验不足的开发人员来说,这个类可能很具有挑战性,难以快速理解。
  • 类中存在仅在某些方法中使用的字段。
  • 类有仅对参数进行操作的静态方法。

然而,顺便提一下,您的代码包含一个switch语句,这表明您可能正在偏离OCP...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接