【问题标题】:Which is the most elegant Design for this simple OOAD problem?对于这个简单的 OOAD 问题,哪个设计最优雅?
【发布时间】:2010-09-22 21:30:38
【问题描述】:

我正在尝试开始使用 OOAD,我想到了这个问题,我不确定我是否能找到一个好的解决方案:(它是一个真实世界案例的超级简化版本)。

一个渔夫用一根鱼竿在池塘里钓鱼。 每次钓线下水时,有阳光下钓到鱼的概率是1/10,晚上钓到鱼的概率是1/20。

要定义哪些类? 我会回答:Fisherman, FishingRod, Pond, Day(日夜建模)。

有哪些方法? 我会回答: Fisherman.Launch(FishingRod), FIshingRod.TryToFish(Pond) 返回布尔值

如何对可能性进行建模? 谁对可能性负责?它不属于渔夫,也不属于池塘。在此示例中,仅与日光有关系,在现实世界中,它可能还与渔夫、钓鱼竿和池塘有关。

如何建模外部因素(日光)?

欢迎任何评论。还有代码示例。

更新: 对问题的第一条评论和 tdammers 的回答迫使我更加具体。 正如我上面写的“它是一个真实世界案例的超级简化版本”,无论如何假设我想稍后增加复杂性,而不是超级增加它,假设增加它足够多以便拥有我列出的所有类上面(例如,因为我记录了池塘里有多少鱼,渔夫有多累……)。无论如何,对我来说最有趣的问题是“如何模拟可能性”和“外部因素”。 对于在 OOAD 方面没有太多技能的人来说,将此视为新手问题。

【问题讨论】:

  • “最优雅的设计”在很大程度上取决于钓鱼领域类库的典型用户想要做什么。他们在制作钓鱼视频游戏吗?大规模的蒙特卡罗模拟和许多模拟试验来研究孵化场枯竭?一个设计可能非常适合一个用例,但过于复杂、不适合甚至无法用于另一个用例。有些设计几乎不适合任何用例,但如果不考虑预期用途,很难说设计是优雅的。
  • 好的,很好。让我更新一下我的问题,以便缩小可能的答案范围。

标签: c# java delphi oop


【解决方案1】:

我会有 Fisherman、FishingRod、FishingLine、Pond、Fish 和“天空”(或“环境”)。

在面向对象的领域,对象通常最终会比你想象的更聪明。渔夫“有”(包含)FishingRod。他将 FishingLine(FishingRod 的一个组件)投射到 Pond 中。池塘“观察”天空以确定是白天还是黑夜,然后掷骰子以确定是否应该将鱼放在线上。

改变的对象层次结构是 FishingLine 可以选择性地包含 Fish,并由 FishingRod 拥有,FishingRod 由 Fisherman 拥有。 Pond 包含 Fish,接收 FishingLines 但不“拥有”它们,也知道但不拥有天空。

随后的方法类似于以下内容:

Fisherman.FishingRod - 一个初始化属性(或一对 getter/setter 方法),用于给 Fisherman 一个 FishingRod 到 FishAt() 一个 Pond。这是可选的;渔夫可以创建自己的钓鱼竿,或者他自己可以从钓鱼竿集合中选择它,而不是给他钓鱼竿。

Fisherman.FishAt(Pond) - 告诉渔夫使用他的 FishingRod 来 Launch() 将 FishingLine 放入池塘,然后 Retrieve() 它可能会得到一条鱼。

FishingRod.Launch(Pond) - 将 FishingRod 的 FishingLine 释放到池塘中。

FishingRod.Retrieve() - 从池塘中检索钓鱼线,返回一条鱼,它也可能什么都没有。

Pond.StockWith(Fish[]) - 给渔夫用鱼竿钓到的池塘鱼。请记住,在 OO-land 中,所有东西要么得到它想要的东西,要么知道如何制造它;如果这是您想要遵循的模型,Pond 可以很容易地创建 Fish,但是这里的用户故事没有说明这是如何发生的(通常意味着它超出了故事的范围)。

Pond.SetFishingLine(FishingLine) - 被 FishingRod 用来将其 FishingLine 放入池塘中。这就是融合了业务逻辑的“驱动功能”。调用此函数时,池塘应询问天空是否是白天,并可能根据一天中给定时间的机会将一条鱼放在钓鱼线上。

Sky.IsDay() - 如果是白天则返回 true,如果是晚上则返回 false。

如果您认为 Pond 不应该直接知道将 Fish 放在 FishingLine 上的确切规则,它可以将 FishingLine 及其 Fish[] 提供给所谓的“纯虚构”。这种制造,“钓鱼逻辑”,将是检查天空并应用规则的制造。在开发中,这通常是一件好事,因为这意味着 FishingLogic 可以在不改变 Pond 的情况下改变,除非 FishingLogic 需要更多来自 Pond 的信息(比如水温)。

各种对象代表现实生活中的各种基本“模式”:

  • Fisherman 是一个“演员”,与我们的用户最接近。像这样的系统的用户基本上站在演员的肩膀上,告诉他该做什么。
  • FishingRod 是“助手”或“实用程序”。它是“工具”的真实模拟,包含状态和业务逻辑的混合体,可帮助它执行非常具体的任务。
  • 此模型中的FishingLine 类似于“请求”或“命令”。它的唯一目的是从一个对象传递给另一个对象,当这种情况发生时,它表明接收者应该采取特定的行动。
  • 鱼是一种“反应”;对请求的答复。可能有,也可能没有。
  • Pond 是一个“存储库”;它包含事物,并根据一组逻辑处理外部对象对这些事物的请求。
  • Sky 是一个“状态桶”。它拥有数据,并通过其接口提供对该数据的访问。
  • FishingLogic 是“纯虚构”;它与我们正在建模的现实世界中的“名词”(对象)没有类似物,并且存在以包含环境规则或在模型对象不必知道如何发生的情况下发生的事情(鱼如何决定上钩?)

【讨论】:

  • 非常感谢,这正是我正在寻找的答案。关于 FishingLogic 的讨论非常有趣。基本上所有在现实世界中不会做的事情(池塘不问天空)都可以(这不是强制性的,当然,这取决于场景的复杂程度)可以委托给“纯粹的制造”。当(我希望)我在现实世界中使用 OOAD 时,我会保留这个答案并在几个月后再次查看它。谢谢。
  • 很好的答案!只是一个烦恼:为什么池塘要决定是否把鱼放在线上?鱼不能自己看着天空,决定去咬一口吗?然后,您最终还可以通过一条鱼的“饥饿”、“咬快乐”、“攻击性”等来确定是否有任何一条或更多条鱼决定上钩。
  • @Marjan 和 Keith:可以这么说,在模型没有鱼之前,一个类必须负责实际上是鱼的决定(去或不上钩),在这个如果 FishingLogic 类有意义吗?当然,如果开始对鱼进行建模,池塘会变得更类似于现实生活中的情况:它不是水和鱼的混合物,而只是鱼的水容器。因此,如果我理解得越清楚,域中的类与“现实世界”的复杂性匹配得越多,对象的行为就会越像在现实世界中那样“完全”。
  • @user193655:如果你的模型中没有任何鱼类,那么是的,当然你需要在别处建模,无论钓鱼线铸造是否产生结果。在这种情况下,我更喜欢单独的类,例如 FishingLogic,而不是将逻辑添加到另一个类(例如 Pond)。它使设计更简洁,更容易适应/扩展。
  • 由于逻辑的性质,我将逻辑附加到 Pond(直接或通过 FishingLogic)。要求规定演员有一定的机会钓到一条鱼。这意味着 Pond 或 FishingLogic 必须至少对上线的 Fish 有一定的控制权。如果一条鱼很聪明,并且可以决定是否咬人,那么您可以天真地创造一种情况,让每条鱼都必须做出决定。这导致捕获鱼的总体概率不同 (1-(.9^numberOfFish))。 Pond 或 FishingLogic 必须只向一条鱼展示这条线并让它决定。
【解决方案2】:

没有课程。没有方法。一个函数,三个参数。

bool launch_rod(bool daytime, float chance_daytime, float chance_night) {
    float chance = daytime ? chance_daytime : chance_night;
    float cast = random_float();
    return cast < chance;
}

仅此而已。 也许,只是也许,将整个事物包装在一个类中,并为该类创建 chance_daytime 和 chance_night 属性。考虑到规格,其他任何东西都是过度设计的。

【讨论】:

  • Lol :) OP 说“这是真实世界案例的超级简化版本”。所以当然是过度设计了。但问题仍然存在。
  • 这是一个OOA&D问题。根据定义,他们想要的答案将是一个过度设计的答案。
【解决方案3】:

Fish 类还可以“仰望天空”来决定它有多饿。 Pond 类似乎相当惰性。

Fisherman, Rod, Line, Fish, Sky

Fisherman.cast(), .drinkBeer(), .chooseRod(), .addLineToRod()
Rod.cast()
Line.cast()
Fish.bite()
Fish.checkDay()
Sky.isDay()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 2011-10-31
    • 2011-08-05
    相关资源
    最近更新 更多