【问题标题】:Is Car class violating Single Responsibility Principle?汽车类是否违反单一责任原则?
【发布时间】:2013-01-14 19:08:45
【问题描述】:

即使我认为我理解单一职责原则和高/低内聚原则,以下问题仍然让我有些困惑

1) 假设Planet 和Bird 属性在Car 类中任意/随机放置(即Car 中没有代码需要或操作这两个属性返回的两个对象) - 在其他也就是说,Planet 和 Bird 属性不属于 Car 类

一)

SRP 指出对象应该只有一个改变的理由。

public class Car
{
    public void StartEngine()
    { ... }

    private Planet Planet
    {
        get { ... }
    }

    private Bird Bird
    {
        get { ... }
    }
}

Car 类是否违反了 SRP?我会说它不会破坏 SRP,因为对 Planet 或 Bird 实例的任何更改都不会传播到 Car 类?

b)

内聚是指方法和类级别的相关程度如何 变量在一个类中。在高度内聚的类中,所有方法 和类级别的变量一起使用来完成一个特定的 任务。在低内聚函数的类中随机插入 进入一个类并用于完成各种不同的任务

假设即使Car 类包含这两个随机属性,它仍然只完成一个特定任务(或几个密切相关的任务):

我们会说Car 具有低凝聚力,即使它仍然执行特定任务(或几个密切相关的任务)?

2) 假设Planet 和Bird 属性被Car 实例的方法用来完成特定任务,那么Car 将具有高内聚性,即使在概念上这两个属性不属于Car(因此最好将Planet 和Bird 实例作为参数传递给对其进行操作的Car 方法)

谢谢

HELTONBIKER:

1)

当您将 Bi​​rd 和 Planet 封装在 Car 中时(如果它们是 private),所以现在 Car 类有三个改变的理由:

我看不出Car 有三个改变的理由,因为在我的第一个问题Car's 方法甚至不对这两个属性进行操作,因此对Planet's 和Bird's 公共API 的任何更改都不会'不影响Car类?

2)

The problem here has two components:
1.  Bird and Planet are contained (as opposed to aggregated) in Car class;
2.  Bird and Planet are not conceptually related to Car, much less by some containment relationship.

a)这令人困惑:Car 的机会(至少在我的第一个问题)是否由于修改 Planet 或 Bird 实例而必须修改,无论 @987654351 是否完全相同@ 和 Bird 实例是包含还是聚合?

b) 在第二个问题中,Car 的方法确实对这两个属性进行操作以执行单个特定任务,所以它们在概念上至少不是有些相关吗?您是否会说即使在第二个问题中类具有低凝聚力,即使它只执行一个单一任务(并且正在使用这两个属性来完成任务)?

【问题讨论】:

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


    【解决方案1】:

    汽车类确实具有低内聚性,因为它指的是与其职责集完全不同的类。它还具有更高的耦合面,因为由于 Planet 和 Bird 是公共的,您已经向消费者提供了对这些属性的访问权限,这意味着您现在向任何消费者添加了另外两个“更改原因”,无论是否汽车内部使用这些。

    无论如何,SRP 已被违反,仅仅是因为 Car 现在负责“获取行星或鸟类的方法”,而无视任何耦合/内聚论点。

    【讨论】:

    • 好的,通过修改,我想说“汽车没有严格违反 SRP,但它确实表现出低内聚力”。基本上,您现在拥有相当于未使用的私有成员变量。不是一个好的做法,但不会直接影响 SRP。
    • 不,如果汽车需要一只鸟和一个星球来完成给定的责任,它绝对需要访问这些类型。在这种情况下,尽管给定示例类名称可能会显得“奇怪”,但不一定会降低内聚力。
    • @user437291 “最好将它们作为参数传递给 Car 方法?”你可以看看“聚合优于组合”的原则,例如,这里:ootips.org/uml-hasa.html
    • @user437291 嗯,无论哪种方式,它都需要访问这些类型才能执行它的职责 - 如果它不需要维护对它们的引用,那么坚持下去将是一个糟糕的设计选择对他们来说,但凝聚力更多地与相互作用有关,而不是纯粹的组合。这就是内聚和耦合这两个术语开始有点模糊的地方——在某种程度上,它们描述了同一个“问题”的两个方面
    • @user437291 Ref: en.wikipedia.org/wiki/… - 通过让 Car 与 Planet 和 Bird 交互,COUPLING 上升,因为它正在与更多事物对话。 COHESION 可能会或可能不会改变,因为从这个无所事事的例子中,很难说汽车/鸟/行星的相互作用是否相关。如果所讨论的方法类似于“根据行星和鸟类名称的组合命名汽车模型”,那么(可以说)这是一个有凝聚力的,如果非常奇怪的方法
    【解决方案2】:

    1) 我想说Car 不能容纳Planet 和Bird。这样 Car 有两个不同的职责:汽车功能和持有一些奇怪的物体。 应该有一些其他对象/类可以保存世界中的对象:例如:class WorldContainer

    2) 我会说你的两个例子的凝聚力都很低。管理汽车和一些不同的对象应该使用其他一些界面来完成。将它们粘合在一起的界面。

    【讨论】:

    • " ...汽车有两种不同的责任" 但是在 SRP 的上下文中,责任被定义为“改变的理由”。因此,我们不能说 Car 仍然只有一个改变的理由吗?
    • 但是如果你改变了 Bird 类的名称呢?那么您将不得不更改 Car 类。这当然是一个简单的例子,但不知何故你会感觉到这种耦合有问题。另一件事是 Car 为 Bird 和 Planet 成员提供了一些公共访问权限,这有点奇怪。
    • 我同意如果您更改 Bird 类的公共接口(甚至只是 Bird 的名称),那么更改将波及 Car 类。但是任何引用其他类的类都可以被认为是高度耦合的,即使它只使用被引用类的公共接口
    • 高耦合类有无意义的连接,我们需要将它们最小化。如果一个类的连接最少,那么很容易对变化做出反应。
    【解决方案3】:

    SRP 意味着一个类应该只有一个改变的理由。

    所以,对 Car 类的修改应该意味着 Car 概念模型发生了变化。

    但是,当您将 Bi​​rd 和 Planet 封装在 Car 中时(如果它们是私有的则更糟),所以现在 Car 类有三个原因需要更改:

    1. 修改汽车概念模型和/或行为;
    2. 修改 Bird 概念模型和/或行为;
    3. 修改 Planet 概念模型和/或行为;

    这里的问题有两个组成部分:

    1. Bird 和 Planet 包含(而不是聚合)在 Car 类中;
    2. Bird 和 Planet 在概念上与 Car 无关,更不用说某些包含关系。

    或者,坦率地说(我希望你这样做是一种教学练习),显示的架构根本没有意义。


    聚合示例(在 Python 中)。非内聚类在引用它们的 Car 类定义之外定义。汽车依赖于鸟和行星,但现在鸟和行星独立存在。

    class Bird():
        hasWings = True
    
    class Planet():
        isFlat = False
    
    class Car():
        owner = Bird()
        substrate = Planet()
    

    参数传递示例(仅汽车类,假设其他类与上述类似)。现在 Car 构造函数(python 中的 __init__ 方法)将实例作为参数。这可能会或可能不会被首选。依赖和耦合仍然存在,但现在可能更加具体。

    class Car():
        def __init__(bird, planet)
            owner = bird
            substrate = planet
    

    最后,整个内聚和耦合问题与软件本身并没有太大关系,而是与开发人员有关。编译器不会介意您的命名空间、项目文件夹和文件分发是否混乱,只要它“编译”即可。但是像你做的那样做没有任何意义(在 Car 类中放置一个 Bird 和一个 Planet 类)。刚开始,您对每个类的版本控制会非常混乱。

    所以,你不应该违反的纯洁不是为了它而写在书本上的。这种纯度是(或应该是)人类与机器指令斗争的结果。面向对象和一般的软件架构不是为机器设计的,而是为开发人员的(部分)思想设计的。

    【讨论】:

    • 我会尝试编辑我的答案(我也需要为我澄清它:o)
    • 我真的要睡在这个上面然后回来:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多