【问题标题】:What is the use of single responsibility principle?单一责任原则有什么用?
【发布时间】:2018-09-09 18:12:18
【问题描述】:

我正在尝试理解单一职责原则,但我很难掌握这个概念。我正在阅读 Lucian-Paul Torje;Adrian Ianculescu;Kamalmeet Singh 所著的“Java 中的设计模式和最佳实践”一书。

在这本书中,我正在阅读单一职责原则章节, 他们有一个汽车类,如下所示:

他们说 Car 同时具有 Car 逻辑和数据库操作。将来如果我们想更改数据库,那么我们需要更改数据库逻辑,并且可能还需要更改汽车逻辑。反之亦然...

解决方案是创建两个类,如下所示:

我的问题是,即使我们创建了两个类,假设我们正在向 CAR 类添加一个名为“price”的新属性 [或者将属性“model”更改为“carModel”] 那么你不认为我们也需要更新 CarDAO 类,如更改 SQL 等。

那么这里的 SRP 有什么用呢?

【问题讨论】:

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


    【解决方案1】:

    好问题。

    首先,请记住,这是本书中的一个简单示例。读者可以对此进行一些扩展并想象更复杂的场景。在所有这些场景中,进一步假设您不是团队中唯一的开发人员;相反,您在一个大型团队中工作,开发人员之间的交流通常采用协商类接口的形式 API、公共方法、公共属性、数据库模式。此外,您经常需要担心回滚、向后兼容性以及同步发布和部署。

    例如,假设您想将数据库从 MySQL 换成 PostgreSQL。使用 SRP,您将重新实现 CarDAO,更改使用的任何特定方言的 SQL,并保持 Car 逻辑不变。但是,您可能需要在配置中进行一些小改动,以告诉 Car 使用新的 PostgreSQL DAO。一个合理的 DI 框架会让这一切变得简单。

    假设,在另一个示例中,您希望将 CarDAO 委托给另一个开发人员以与 memcached 集成,这样读取虽然最终是一致的,但速度很快。同样,该开发人员不需要了解Car 中的业务逻辑。相反,它们只需要在CarDAO 的CRUD 方法后面进行操作,并且可能在CarDAO API 中声明更多的方法,具有不同的一致性保证。

    假设,在另一个示例中,您的团队聘请了一位专攻合规法律的数据库工程师。在为即将到来的 IPO 做准备时,数据库工程师的任务是保存公司 35 个数据库中所有表的所有更改的审计日志。有了 SRP,我们勇敢的 DBA 就不必担心使用我们的任何表的任何业务逻辑;相反,它们的变异跟踪魔法可以巧妙地注入到整个 DAO 中,使用装饰器或其他方面编程技术。 (顺便说一下,这也可以在 SQL 接口的另一端完成。)

    好的,最后一个 - 假设现在团队中有一名系统工程师,负责在 AWS 中跨多个区域(数据中​​心)分片这些数据。该工程师可以进一步采用 SRP 并添加一个组件,该组件的唯一作用是为每个 ID 告诉我们每个实体的主区域。每次我们进行跨区域读取时,新组件都会碰到一个计数器;每周,一个自动化工具都会将跨区域频繁读取的数据迁移到新的主区域以减少延迟。

    现在,让我们进一步发挥我们的想象力,假设业务正在蓬勃发展 - 突然间,您正在为一家拥有多个部门跨越多个国家的财富 500 强公司工作。财务部的业务分析师希望使用您的表格在其 IPO 后投资者报告中绘制汽车销售季度增长情况。与其让他们访问Car(因为用于报告的逻辑可能与用于准备数据以在 Web UI 上呈现的逻辑不同),您可以潜在地为CarDAO 创建一个只读接口您现在必须跨部门维护的精心策划的公共属性的简短列表。上帝禁止你必须重命名这些属性之一:为 3 个月的日落计划和许多悲伤的仪表板和深夜升级做好准备。 (并且请不要让他们直接访问实际的 SQL 表,因为隐含的假设是整个表是 公共接口。)糟糕,我的伤疤可能正在显现。

    推论是,如果您需要更改Car 中的业务逻辑(例如,添加一个计算每辆特斯拉在尴尬召回后的较低售价的方法),您会不要碰CarDAO,因为if car.brand == 'Tesla; price = price * 0.6 与数据访问无关。

    补充阅读:CQRS

    【讨论】:

    • 感谢您的详细解释。所以为了简单起见,我在 Car 类中添加了属性,我还需要在 CarDAO 中进行更改,但与以前相比影响会更小。我的理解正确吗?
    • 是的,这是正确的。一个推论是,如果你需要改变业务逻辑,你就不会碰 DAO。
    • 是的,但是模型呢?如果我对模型进行一些更改,那么我也必须更改 DAO 对吗?
    • 如果model指的是Car.model,那么您可以在不改变Car接口的情况下重命名CarDAO中的列(反之亦然)。
    • 哦,我明白了。在这种情况下,必须在两侧进行一些更改,而对于 SRP,有可能只需要在一侧进行一些更改。例如,您可以将decade 属性添加到派生自Car.yearCar,而不更改CarDAO
    【解决方案2】:

    要添加新属性,只有当该属性应该保存到数据库时,您才需要更改这两个类。如果它是业务逻辑中使用的属性,则无需更改 DAO。此外,如果您将数据库从一个供应商更改为另一个供应商或从 SQL 更改为 NoSQL,您将只需要在 DAO 类中进行更改。如果您需要更改某些业务逻辑,则只需更改Car 类。

    【讨论】:

    • 所以简单地说,他们的 SRP 帮助我们减少对更改的影响。我说的对吗?
    • 是的,为了减少影响,使多个开发人员的并行开发更快、更容易(如果一个开发人员处理 Car 类而另一名开发人员处理 CarDAO 类,请避免合并冲突)
    【解决方案3】:

    Single responsibility principle 正如 Robert C. Martin 所说,意味着

    一个类应该只有一个改变的理由。

    牢记这一原则通常会导致类更小且内聚性更高,这反过来意味着更少的人需要同时处理这些类,并且代码变得更加健壮。

    在您的示例中,将数据访问业务逻辑(价格计算)逻辑分开意味着您在进行更改时不太可能破坏另一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多