单一职责原则在 OOP 中有效吗?

Does the Single Responsibility Principle work in OOP?

我正在努力理解如何使单一职责原则与 OOP 一起工作。

如果要完全遵循这个原则,那不就剩下很多类,很多可能只有一个方法吗?

如果我们不完全按照原则来做,那么原则中的意义何在?

一个class应该处理一个主题,这是它的单一职责。 class Car 可以包含方法 startEngine() 或属性 weight:

class Car
{
    int weight;
    void startEngine();
    void stopEngine();
}

当然你可以再分解这个class。例如,为引擎定义一个 class:

class Engine
{
    start();
    stop();
}

class Car
{
    Engine engine;
    int weight;
}

但是您不应定义单独的classes来启动和停止引擎,例如:

class EngineStop
{
    stop();
}

class EngineStart
{
    start();
}

class Car
{
    EngineStart engineStart;
    EngineStop engineStop;
    int weight;
}

原理说要尽可能合理的分解classes,达到抽象的目的。抽象得太深违反了这个原则。

这是对单一职责原则的常见误解之一,即 class 应该只有一个功能。一个class只负责一件事并不意味着一个class应该只有一个功能。单一职责原则意味着在逻辑上将您的功能分成不同的 classes 而不是混淆事物。

一个非常简单的例子是,假设您正在创建一个计算器,您可以在同一个 class 中添加纯粹用于计算器的所有功能(加、减、乘、除等)。您无需为计算器的每个功能创建单独的 class。但是,如果你想将计算结果打印到打印机,那么不要在计算器 class 中编写打印到打印机的逻辑,因为打印到打印机不是计算器 class 的责任。为打印机创建一个单独的class,并在class中编写与打印相关的逻辑。 如果您在计算器中编写打印功能 class 那么您就违反了单一职责原则。

我喜欢这样表述单一职责原则:“你写的每一件事——每个模块、class、接口或方法,都应该有 一个工作。它应该完成整个工作,那个工作。

注意你写的这些东西有的大(模块),有的小(方法),有的介于两者之间(classes),还有一些大的东西是由小的东西组成的.

这不是问题,因为工作或职责也有各种规模,并且可以按层次分解。例如,警察部队的工作是 "protect and serve"——一项工作,分解为 "patrol the streets"、"solve crimes" 等,每个工作可以由不同的单位处理。这就需要协调(不同的工作),每个单位的工作分解为个别官员的工作等

对于每一项大工作,都有很多方法可以将其分解为更小的工作,并且每一个工作都可以通过遵循 SRP 和其他 SOLID 原则的软件设计来建模。决定如何分解问题是软件设计艺术的重要组成部分。

我倾向于将单一职责原则(以及很多经验法则、模式和模型)视为思考问题的一般方式。它旨在 指导您改进 您的代码,但不一定准确规定解决方案的外观。因为归根结底,软件设计是一个邪恶而主观的问题。

我不认为 SRP 应该总是导致一堆单一功能 classes。如果您有一些想要抽象的复杂逻辑,有时可能会发生这些情况。不过,一般来说,您应该倾向于 class 具有 高度内聚功能 的方法,其中方法:

  1. 都与相同的抽象相关

    • 不要在同一个 class.
    • 中混杂彼此不相关的功能
    • 将业务逻辑之类的东西与 UI 分开,与 I/O
    • 分开
  2. 共享共同依赖项

    • 相关功能通常具有相关依赖性。
    • 当我开始获得超过 7 个依赖项时,我通常会考虑拆分 class。通常有一个 class 我可以提取以将一些依赖项移动到一个更具凝聚力的单元中。
  3. 所有操作都在同一抽象级别

    • 你的抽象应该是分层的;一些 classes 应该协调其他 classes 来完成工作流程。其他 classes 将处理具体的实施级细节。
    • 避免编排风格 classes 进入细节。您不希望详细操作与更复杂操作的抽象并存(在同一个函数中,甚至是同一个 class)。

您实际上想要尝试尽可能多地分组功能,同时保持上述条件并牢记您的抽象的可理解性。单一职责原则的最终目标是帮助管理复杂性并使代码更易于理解。

在分解职责之前,您应该了解 class 应该做什么。正如马特在他的回答中提到的那样,它应该做那件事,而且只做那件事。以最简单的方式实施该想法,使其发挥作用。那就是你将编写的代码分解为职责的时候。

分解代码时要考虑的重要事项是确保分解后代码可读。

我引用 Robert C. Martin 的书 Clean Code:

The Single Responsibility Principle (SRP) states that a class or module should have one, and only one, reason to change. This principle gives us both a definition of responsibility, and a guidelines for class size. Classes should have one responsibility—one reason to change.

单一职责术语是由 Rober C. Martin 引入的。这一原则的主要格言是改变的理由。一个 class 或模块应该有一个,而且只有一个理由成为 changed.The 这一原则的主要好处是使 class 更健壮。对一个 class 或模块的更改不会破坏另一部分。

它防止一个对象成为上帝对象,这是反模式的一个例子。它还可以防止馄饨代码(包含许多微小的、紧密耦合的对象的源代码)。

因此,单一职责原则是 OOP 中良好源代码设计的重要组成部分。