有超过 1 种方法不会破坏单一职责原则吗?
Doesn't having more than 1 method break the Single Responsibility Principle?
我对单一职责原则很困惑。 原则指出 class 更改的原因应该只有一个。
我面临的问题是,对方法的任何更改或做事中的任何逻辑更改都会更改 class。例如,考虑以下 class:
class Person{
public void eat(){ };
public void walk(){ };
public void breathe(){ };
public void run(){ };
public void driveCar(Car car){ };
}
Bob 大叔将其描述为 应该只有一个 person/Actor 负责更改 。我有以下两个问题:
- 以上class谁是actor/Person谁可以负责更改?
- 进食、呼吸或行走的任何逻辑改变都不会改变 class 人吗?那么这是否意味着每一种方法都是改变的理由,因为它做事的逻辑可能会改变?
有趣的问题。来自 "Uncle Bob" Martin 的 quote 是:
A class should have one, and only one, reason to change.
可以将此解释为您的 Person
class 有五个更改原因:您可能想要更改 eat
方法,或更改 walk
方法,或breathe
方法,或run
方法,或driveTheCar
方法。但这太狭隘了,并没有真正理解 Martin 所说的 "reason to change".
的意思
更改 class 的原因意味着人类程序员更改它的动机。您不会仅仅因为有改变 eat
方法的动机而改变 eat
方法;您可以更改它以实现有关程序所需行为的某些目标。
如果 Person
class 模拟一个人进行某种模拟,那么你改变它的动机就是你想要 "to change how people's actions are modelled in the simulation". 您对 class 所做的每一个 更改都会出于该原因,无论您更改了一种方法还是多种方法;所以 Person
class 只有一个 "reason" 需要改变,满足 SRP。
如果Person
class有一些其他的方法比如在屏幕上画人物,那么你可能还需要"to change the graphical appearance of your simulated people".这与改变你的模拟模拟人们行为的方式的动机完全不同,所以 class 有两个责任,违反了 SRP。
改变的原因是什么
- For the above class who is the actor/Person who can be responsible for the change?
一个Actor是一个用户(包括客户、利益相关者、开发人员、组织)或一个外部系统。 我们可以争论人是否是系统,但事实并非如此。
另请参阅:Use case。
- Wouldn't any change in the logic of eating, breathing or walking change the class Person? So doesn't that mean that every method is a reason to change as its logic to doing things might change?
不,方法不是改变的理由。方法是可以改变的东西……但为什么会这样呢?什么会触发开发人员更改它?
单一职责原则的一部分是代码最多只能与一个外部系统交互。 请记住,并非所有参与者都是外部系统,但是,有些是。我认为大多数人会发现 SRP 的这一部分很容易理解,因为与外部系统的交互是我们可以在代码。
然而,这还不够。例如,如果您的代码必须计算税收,您可以在代码中硬编码税率。这样,它就不会与任何外部系统交互(它只是使用常量)。然而,在一项税制改革之后,政府被披露为更改代码的原因。
您应该能够做的是交换外部系统(可能需要一些额外的编码工作)。例如,从一个数据库引擎更改为另一个。但是,我们不希望这些更改中的任何一项转化为对代码的完全重写。更改不应传播,进行更改不应破坏其他内容。为确保这一点,我们希望隔离处理数据库引擎(在本例中)的所有代码。
Things that change for the same reasons should be grouped together, things that change for different reasons should be separated.
-- Robert C Martin
我们可以对上面的政府示例做类似的事情。我们可能不希望软件读取会议纪要,相反,我们可以让它读取配置文件。现在外部系统是文件系统,会有与之交互的代码,而该代码不应该与其他任何东西交互。
我们如何确定这些改变的原因?
您的代码由一组要求定义。有些是功能性的,有些则不是。如果这些要求中的任何一个发生变化,您的代码也必须更改。更改需求的原因就是更改代码的原因。
注意:您可能没有记录所有需求,但是,未记录的需求仍然是一个需求。
然后,你需要知道这些需求是从哪里来的。谁或什么可以改变他们?这些就是你改变的原因。这可能是公司政治的变化,可能是我们正在添加的功能,可能是新法律,可能是我们正在迁移到不同的数据库引擎或不同的操作系统,翻译成另一种语言,适应另一个国家等
其中一些是您的代码与之交互的外部系统(例如数据库引擎),一些不是(公司的政策)。
责任怎么办
您想隔离他们。因此,您将拥有与数据库交互的代码,仅此而已。您将拥有实现业务规则的代码,仅此而已。等等。
意识到即使代码的每个部分的实现都依赖于外部的东西,它们的接口也不一定。因此,定义接口并注入依赖项,这样您就可以更改每个部分的实现而不必更改其他部分……也就是说,部分代码的实现不应成为更改代码其他部分实现的理由.
注意:代码的任何部分都不应具有多重职责。让你的部分代码处理每个职责,让你的部分代码负责将其他部分组合在一起。同样,如果您的代码的一部分没有责任……就没有理由保留它。因此,您的代码的每一部分都应该只负责一个。
对于你的代码,问问自己,Person
class的要求是什么。它们完整吗?他们从哪里来?他们为什么要改变?
推荐观看
关于单一职责原则更权威的解释,参见2015年挪威开发者大会Robert C Martin - The Single Responsibility Principle(51分8秒,英文)
我对单一职责原则很困惑。 原则指出 class 更改的原因应该只有一个。
我面临的问题是,对方法的任何更改或做事中的任何逻辑更改都会更改 class。例如,考虑以下 class:
class Person{
public void eat(){ };
public void walk(){ };
public void breathe(){ };
public void run(){ };
public void driveCar(Car car){ };
}
Bob 大叔将其描述为 应该只有一个 person/Actor 负责更改 。我有以下两个问题:
- 以上class谁是actor/Person谁可以负责更改?
- 进食、呼吸或行走的任何逻辑改变都不会改变 class 人吗?那么这是否意味着每一种方法都是改变的理由,因为它做事的逻辑可能会改变?
有趣的问题。来自 "Uncle Bob" Martin 的 quote 是:
A class should have one, and only one, reason to change.
可以将此解释为您的 Person
class 有五个更改原因:您可能想要更改 eat
方法,或更改 walk
方法,或breathe
方法,或run
方法,或driveTheCar
方法。但这太狭隘了,并没有真正理解 Martin 所说的 "reason to change".
更改 class 的原因意味着人类程序员更改它的动机。您不会仅仅因为有改变 eat
方法的动机而改变 eat
方法;您可以更改它以实现有关程序所需行为的某些目标。
如果 Person
class 模拟一个人进行某种模拟,那么你改变它的动机就是你想要 "to change how people's actions are modelled in the simulation". 您对 class 所做的每一个 更改都会出于该原因,无论您更改了一种方法还是多种方法;所以 Person
class 只有一个 "reason" 需要改变,满足 SRP。
如果Person
class有一些其他的方法比如在屏幕上画人物,那么你可能还需要"to change the graphical appearance of your simulated people".这与改变你的模拟模拟人们行为的方式的动机完全不同,所以 class 有两个责任,违反了 SRP。
改变的原因是什么
- For the above class who is the actor/Person who can be responsible for the change?
一个Actor是一个用户(包括客户、利益相关者、开发人员、组织)或一个外部系统。 我们可以争论人是否是系统,但事实并非如此。
另请参阅:Use case。
- Wouldn't any change in the logic of eating, breathing or walking change the class Person? So doesn't that mean that every method is a reason to change as its logic to doing things might change?
不,方法不是改变的理由。方法是可以改变的东西……但为什么会这样呢?什么会触发开发人员更改它?
单一职责原则的一部分是代码最多只能与一个外部系统交互。 请记住,并非所有参与者都是外部系统,但是,有些是。我认为大多数人会发现 SRP 的这一部分很容易理解,因为与外部系统的交互是我们可以在代码。
然而,这还不够。例如,如果您的代码必须计算税收,您可以在代码中硬编码税率。这样,它就不会与任何外部系统交互(它只是使用常量)。然而,在一项税制改革之后,政府被披露为更改代码的原因。
您应该能够做的是交换外部系统(可能需要一些额外的编码工作)。例如,从一个数据库引擎更改为另一个。但是,我们不希望这些更改中的任何一项转化为对代码的完全重写。更改不应传播,进行更改不应破坏其他内容。为确保这一点,我们希望隔离处理数据库引擎(在本例中)的所有代码。
Things that change for the same reasons should be grouped together, things that change for different reasons should be separated. -- Robert C Martin
我们可以对上面的政府示例做类似的事情。我们可能不希望软件读取会议纪要,相反,我们可以让它读取配置文件。现在外部系统是文件系统,会有与之交互的代码,而该代码不应该与其他任何东西交互。
我们如何确定这些改变的原因?
您的代码由一组要求定义。有些是功能性的,有些则不是。如果这些要求中的任何一个发生变化,您的代码也必须更改。更改需求的原因就是更改代码的原因。
注意:您可能没有记录所有需求,但是,未记录的需求仍然是一个需求。
然后,你需要知道这些需求是从哪里来的。谁或什么可以改变他们?这些就是你改变的原因。这可能是公司政治的变化,可能是我们正在添加的功能,可能是新法律,可能是我们正在迁移到不同的数据库引擎或不同的操作系统,翻译成另一种语言,适应另一个国家等
其中一些是您的代码与之交互的外部系统(例如数据库引擎),一些不是(公司的政策)。
责任怎么办
您想隔离他们。因此,您将拥有与数据库交互的代码,仅此而已。您将拥有实现业务规则的代码,仅此而已。等等。
意识到即使代码的每个部分的实现都依赖于外部的东西,它们的接口也不一定。因此,定义接口并注入依赖项,这样您就可以更改每个部分的实现而不必更改其他部分……也就是说,部分代码的实现不应成为更改代码其他部分实现的理由.
注意:代码的任何部分都不应具有多重职责。让你的部分代码处理每个职责,让你的部分代码负责将其他部分组合在一起。同样,如果您的代码的一部分没有责任……就没有理由保留它。因此,您的代码的每一部分都应该只负责一个。
对于你的代码,问问自己,Person
class的要求是什么。它们完整吗?他们从哪里来?他们为什么要改变?
推荐观看
关于单一职责原则更权威的解释,参见2015年挪威开发者大会Robert C Martin - The Single Responsibility Principle(51分8秒,英文)