【问题标题】:Does the Single Responsibility Principle work in OOP?单一职责原则在 OOP 中有效吗?
【发布时间】:2018-03-14 10:48:33
【问题描述】:

我很难理解如何使单一职责原则与 OOP 一起工作。

如果我们要遵循这个原则,那么我们不是留下了很多类,其中很多可能每个只有一个方法吗?

如果我们不完全遵循原则,那么原则的意义何在?

【问题讨论】:

  • 这里有人写过用 SRP sklivvz.com/posts/…987654321@
  • 基本层面的观点是鼓励编写具有高凝聚力的对象(包含的“部分”高度相关并且属于一起)
  • tag wiki

标签: oop design-patterns solid-principles single-responsibility-principle


【解决方案1】:

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

请注意,您编写的这些东西有些很大(模块),有些很小(方法),有些介于两者之间(类),有些大东西是由较小的东西组成的。

这不是问题,因为工作或职责也有各种大小,并且可以分层分解。例如,警察的工作是“保护和服务”——一项工作,分解为“巡逻街道”、“破案”等,每个工作都可以由不同的单位处理。这就产生了协调的需求(不同的工作),每个单位的工作分解成个别军官的工作,等等。

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

【讨论】:

  • 这正是我感到困惑的地方——不同职位的职责不同。是否有一个特定的点您应该停止分解职责,或者只是按照自己认为正确的方式去做?
  • 有一个权衡:将一个工作分解成更小的工作使大工作更容易理解,但增加了工作的数量,这会使整个系统更加复杂。有一个甜蜜点,每项工作的解决方案都适合您的头脑,并且将其分解得更多会使工作变得更加困难,因为您需要进行的更改会弄乱更多的事情。停在那里。这对每个人来说都是一个略有不同的地方。
  • 当然。我相信理想情况下将大任务分成较小的任务,这将限制每个功能只做一项工作。每个工作都是原子的,意味着逻辑上孤立和完整。这将是一个理想的场景,但在实践中我们会尽量接近这一点。
  • 我做的最好的思维练习之一就是尝试预测系统中可能发生的变化。然后我尝试了解如何分离职责,因此如果确实发生了变化,我不需要重构现有代码,但我可以删除现有工作,并用新工作替换它们,尽可能保持它们之间的接口完整。这让我想起了 Justin Searls 今年在 RailsConf 上的一次演讲,youtube.com/watch?v=V4fnzHxHXMI。并非所有内容都直接相关,但可能有助于您的决策
  • @jaco0646 这是一个正确的观点,但我喜欢使用“工作”这个词,因为一个工作意味着一个老板。我认为在设计软件时没有必要将这些原则分开考虑,但既然你提到了它,坚持这两个原则的理由就不同了。
【解决方案2】:

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

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

当然,你可以更多地分解这个类。例如为引擎定义一个类:

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

class Car
{
    Engine engine;
    int weight;
}

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

class EngineStop
{
    stop();
}

class EngineStart
{
    start();
}

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

该原则说尽可能合理地分解类以实现抽象。抽象太深违反了这个原则。

【讨论】:

  • 我理解SRP,但是“您不应为启动和停止引擎定义单独的类”来自哪里?
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    单一职责一词是由 Rober C. Martin 引入的。这一原则的主要座右铭是改变的理由。一个类或模块应该有一个,并且只有一个改变的理由。这个原则的主要好处是使类更加健壮。一个类或模块中的更改不会破坏另一部分。

    它防止对象成为反模式示例的上帝对象。它还可以防止 Ravioli 代码(包含许多微小、紧密耦合的对象的源代码)。

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

    【讨论】:

      【解决方案5】:

      在分解职责之前,您应该对类应该做什么有一个概念。正如马特在回答中提到的那样,它应该做那件事,而且只有那件事。以最简单的方式实现这个想法,让它发挥作用。这就是你开始将编写的代码分解为职责的时候。

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

      【讨论】:

        【解决方案6】:

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

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

        1. 都与相同的抽象相关

          • 不要在同一类中混杂不相关的功能。
          • 将业务逻辑等内容与 UI 与 I/O 分开
        2. 共享公共依赖项

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

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

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

        【讨论】:

          【解决方案7】:

          我引用 Robert C. Martin 的书 Clean Code

          单一职责原则 (SRP) 指出,一个类或 模块应该有一个,也只有一个,改变的理由。这个原则 给了我们责任的定义和指导方针 班级规模。类应该有一个责任——一个理由 改变。

          【讨论】:

            猜你喜欢
            • 2011-11-16
            • 1970-01-01
            • 1970-01-01
            • 2016-01-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-16
            相关资源
            最近更新 更多