【问题标题】:How detailed should a object be?一个对象应该有多详细?
【发布时间】:2009-12-10 12:37:53
【问题描述】:

我正在开发一种 HMI 应用程序,并正在创建对象来定义特定的机器。为了争论,假设这是一辆汽车。

引擎的对象是显而易见的。引擎上有一些常见的传感器,这是一些安装到引擎对象上的一些属性的对象。油门属性当然是一个输入。

这辆车至少有一扇门。每扇门都可以有一个窗户,它可以是可打开的,也可以是电动的。如果它是电动的,它将依赖于汽车的动力来运行。

现在,我应该将门作为汽车对象的属性公开,还是将其保密并让汽车对象在 OpenDoor 和 RollDownWindow 函数中操作门是最明智的?事件呢?我应该在引擎上公开事件,例如 LowOnOil 事件,还是应该在汽车对象中处理它,而女巫又可能有像 EngineIsLowOnOil 这样的事件?

你会怎么做?

【问题讨论】:

  • 没有单一的最佳答案,我认为这属于社区 wiki。
  • 有一个最佳答案:John R. Strohm's。

标签: c# oop


【解决方案1】:

这个问题以及所有类似的问题都可以通过考虑构建模型的原因来回答。与您要解决的问题隔离开来创建模型绝对没有意义,而且通常不可能这样做。

例如,如果您正在构建一个电子燃油喷射控制系统,那么汽车上的车门数量(甚至可能是汽车本身)是无关紧要的,不应该建模。

【讨论】:

  • 燃油喷射系统将是包含在发动机中的一个对象。然后引擎对象应该将燃料系统作为属性公开,还是应该将其保留为私有?假设它是涡轮增压系统,而您想要涡轮压力......只暴露需要的东西,还是整个燃油系统?
  • @rozon,一般的经验法则是只公开你需要的。如果您需要在以后暴露燃油系统,那么这样做应该很容易。
  • 不知道域就不可能说。您似乎相信有一种“正确”的方式来产生对象设计。事实并非如此。
  • @Neil,我正在谈论这个主题......看看其他开发人员对这个主题的看法从来没有什么坏处...... :)
【解决方案2】:

让我们来看看你打开车门的例子(比如左前)。可以采取多种方法(包括您建议的方法):

  1. Car.OpenFrontLeftDoor
  2. Car.OpenDoor(前左)
  3. Car.Door(FRONTLEFT).Open
  4. Car.Part(DOOR_FRONTLEFT).Open
  5. Car.Part(DOOR_FRONTLEFT).DoAction(OPEN)

它们都没有对错,这取决于情况。我相信还有更多的方法。

数字 1 是一种非常硬编码的函数方法。这对于非常简单、固定的情况很有用。但是,如果您的模型需要适应变化,它将变得难以管理。

5 号采用参数化方法。后者更灵活,需要更出色的设计才能实现,但对于一个简单的问题可能有点过头了。

另外请记住,您的汽车对象可以呈现与内部实现不同的外部接口。例如,您可以在内部使用方法 5,但提供一个如 1 中的接口,并在后台转换函数调用(没有双关语)。

最终,做出这样的决定的能力来自经验。让自己接触优秀的 OO 设计,阅读书籍,检查优秀软件的源代码。最重要的是,尝试不同的设计,并亲眼看看哪些是有效的,以及为什么。

【讨论】:

    【解决方案3】:

    爱因斯坦说:“让它尽可能简单,但不要更简单。”

    从一个空对象开始。在开发整个模型时,只向对象添加那些必要的属性。

    【讨论】:

    • 并随时准备重构。这意味着偶尔会决定需要拆分或合并对象。
    【解决方案4】:

    我认为这是一个很难回答的非常笼统的问题。我的最佳答案是“这取决于您的需求和您要解决的问题领域”。

    【讨论】:

      【解决方案5】:

      在你给出的场景中,我通常倾向于在现实生活的情况下考虑它。

      因此,车门不是汽车专用的,即车门可公开进入。汽车不开门(除非它是一辆非常酷的汽车!)用户会开门。因此,门应该是汽车的公共财产。

      在曝光事件方面,这实际上取决于您是否打算处理它们。例如,OnLowOil 事件可能是您希望处理的事件,即通知可能会执行 Car.Engine.FillOil

      的用户

      【讨论】:

      • 关于车门,我发现很难区分汽车和车门。另一方面,发动机;它可能有许多传感器可以导致仪表板上的 NeedService 通知。与其为引擎连接所有事件,不如让汽车在本质上处理它们,然后向用户发出更好(更简单)的事件?
      • 好好想一想,门本身必须成为它自己的对象,因为它将具有诸如 Window/State(open/close) 之类的属性以及诸如 Open/Close 之类的自己的方法。一个很好的例子说明为什么我的车会像许多其他人一样告诉我一扇门是打开的还是没有正确关闭。因此,在那个特定示例中,如果您没有要检查的 Door 对象,您如何知道门是否已关闭?就事件而言,是的,在某些情况下,引擎中的事件最好不要暴露,但有些事件需要您暴露,这取决于您自己的设计。
      【解决方案6】:

      如果有疑问,请从不同的方法考虑问题:“我将如何测试这些对象?” “我将如何测试窗户?” “什么会使测试变得最容易?”作为一个懒惰的程序员,这对我来说是一个非常重要的问题。我希望编写测试非常简单,但在证明我的代码有效方面非常有效。

      在您的情况下,您有一辆可以打开和关闭的车门,并且车门有可以打开和关闭的窗户,它们可能是电动的,也可能不是电动的。你将如何测试“window-down”功能?窗口的存在是否取决于引擎?它甚至依赖于汽车,还是真的只依赖于门?不同的门有不同形状的窗户,有不同的规则或不同的行程吗?不同的门是否使用相同的电动机来驱动窗户,或者左侧安装和右侧安装的窗户电机是否不同?

      考虑到客户可能会给您一个要求,说“只有在引擎运行时,窗户才能工作”,但这是真的吗?在大多数汽车中,当汽车的电源开关打开时,车窗就会工作。换句话说,车窗操作是依赖于发动机中的旋转曲轴,还是实际上依赖于 12 伏的电力?那么,您真的需要汽车中的引擎实例来测试车窗,还是只需要电池?

      一旦您开始提出这些问题,您可能会得出结论,电动车窗值得进行自己的一组测试(上/下/中途/全行程/等),因此它会成为一个很好的成为自己班级的候选人。不同门的数量可能很复杂,因此您认为“门工厂”可能是一个合适的类来创建可以管理所有不同类型的门。孤立地测试事物总是更容易:看看关于发动机油门设置应该是什么来测试窗口功能的更难的问题?传动齿轮,或门开/关状态如何?所有这些排列都使测试变得困难,即使它们与窗口是否工作无关。所以所有这些让我认为窗口类应该尽可能地独立测试。在现实世界中,您只需要 12 伏电压即可测试安装在门上的电动车窗。因此,在您的建模世界中,您只需要一个模拟或假电源对象来提供建模动力,而不是引擎对象或传动齿轮。这是一个更简单的测试。

      考虑到这些想法后,您开始意识到在测试中传递依赖项(这里是窗口测试的电源)使它们易于测试;因此,在您的模型中组装真实组件的方式意味着在工厂中封装结构将是制造汽车的最简单方法;所以构建汽车可能会建议使用抽象工厂模式将所有依赖项注入汽车。

      您甚至可以从带有硬编码引擎和硬编码门的简单但脆弱的 Car 类开始,然后对其进行重构以添加上述测试。您最终可能仍会得到独立的 Car、Door 和 Window 对象,以及基于正在构建的汽车模型创建它们的抽象工厂模式。这种想法被称为“应急设计”。

      【讨论】:

        【解决方案7】:

        我喜欢保持简单。在您的领域/对象中,想想您将如何谈论对象以确定对其建模的方式,您会说我的汽车需要机油还是我的汽车发动机需要机油?

        这取决于域。汽车厂的域名与汽车租赁域名不同。

        我会为每个窗口和事件提供许多窗口属性,以及窗口状态/位置。

        【讨论】:

        • @Burt 10 人中有 9 人会说“我的车需要机油”,尽管实际上它是发动机。这并不完全是错误的,因为本质上是引擎构成了汽车,因此您可以通过公开快捷方法(如果愿意的话)简单地解决该问题,例如 Car.FillOil 内部调用 Engine.FillOil。
        【解决方案8】:

        没有正确或错误的模型;有些模型或多或少对您的特定目的有用。

        正如其他人所说,首先考虑哪些信息与您的需求相关。然后丢弃其余的。删除不必要细节的过程称为抽象。如果我们不执行抽象,我们的模型将与它们所代表的现实世界实体相同!这将是无用的,因为建模的最终目标是获得现实的简化版本,使我们能够在没有现实的情况下对其进行推理。

        【讨论】:

          【解决方案9】:

          我认为这里有几个人提出了相同的观点,但我会再试一次。

          您在问我是否应该公开“门打开”事件之类的问题。这个问题除了另一个问题没有答案:你将如何使用它

          最终,模型是手段而不是目标,因此模型不能在上下文之外定义。即使(特别是)如果您正在构建模型以将其作为独立产品销售,您也必须考虑您的客户将如何使用它——这又是关于上下文的讨论。

          尝试基于其他任何东西(直觉?)回答这些问题将是一种在针头末端数恶魔的练习

          【讨论】:

            猜你喜欢
            • 2010-09-29
            • 2010-09-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-07
            • 2020-05-11
            相关资源
            最近更新 更多