【问题标题】:Should I seperate model classes or have them as a single unit?我应该分开模型类还是将它们作为一个单元?
【发布时间】:2015-09-17 10:59:24
【问题描述】:

我的游戏逻辑模型由多个连接的类组成。有BoardCellCharacter等。Character可以放置(和移动)在Cell(1-1 rel)中。

有两种方法:

  1. 使模型的每个类都实现接口,以便它们可以被模拟,每个类都可以独立测试。它迫使我使每个类的实现不依赖于另一个类。但在实践中,很难避免BoardCells 了解太多,而Characters 知道Cell 存储机制是如何工作的。我有 Character.CellCell.CurrentCharacter 属性。为了让设置器正常工作(而不是递归地),他们应该相互依赖实现。感觉模型逻辑应该被视为一个单元。
  2. 让所有公共成员返回接口,但在内部使用精确的类(可能涉及一些向下转换)。这里的缺点是我应该将整个模型作为一个单独的模型进行测试,并且不能使用模拟来独立测试不同的部分。此外,在模型中使用依赖注入是没有意义的,只是为了从控制器中获取另一个完整的模型实现。

那该怎么办?

更新

您可以提出其他选择。

【问题讨论】:

  • 你能详细解释一下为什么你的模型中的每个类都需要知道其他一些类的实现吗?
  • @YacoubMassad 不是每个人,这更多是关于了解行为Character 知道,当它设置 Cell.CurrentCharacter 时,Cell 会递归回调 Character.Cell 设置器(以确保一致性),因此它应该正确实现其一侧,以免再次使用 Cell.CurrentCharacter
  • Character 什么时候决定设置 Cell.CurrentCharacter?何时调用 Character.Cell 设置器?谁首先调用 Character.Cell?董事会班?谁使用这些类(Board、Cell 和 Character)?你能让所有对这些类的访问都通过 Board 类(如aggregate pattern)吗?
  • @YacoubMassad Board 在每个游戏步骤中移动东西。上层仅使用 CellCurrentCharacter 的 getter。不,我无法通过Board 路由所有访问。
  • @YacoubMassad 实际上我想过让模型与步骤(转向)逻辑分开,只确保模型代码的一致性。所以应该有 2 个用户(玩家和转跑者)应该采用不同的界面。

标签: unit-testing model-view-controller dependency-injection architecture


【解决方案1】:

为什么只有这两个选项?

如果您打算拥有不同版本/类型的类,那么接口/抽象基类是强制共享行为和泛化许多操作的好选择。然而,在彼此不了解的情况下独立构建类的想法是荒谬的。 将类存储/行为与其所属的类/层分开总是一个好主意。例如。数据层中没有业务逻辑代码等,但类需要相互了解才能正常运行。如果你让一切都独立并基于接口,你就会冒过度泛化应用程序并降低效率的风险。 基本上,如果您认为您需要将传入的对象向下转换为一种以上的类型,那么查看设计并查看您是否从即将编写的性能损失和讨厌的转换代码中获得了什么是一个好主意。如果您需要处理所有类型的向下转换对象,您还没有获得任何东西,那么使用多态性和基类是一个更好的方法。

使用接口并不能消除您在测试中的麻烦。无论如何,您仍然必须实例化某些版本的对象来测试单元/板上的大多数功能。对于完整的回归测试,您需要测试每个角色与两者的交互。

不要误会我的意思,你的角色类应该很可能有一个基类或一个接口。所有角色(我敢肯定)都会共享许多动作,并且可以从这种设计中受益。例如。在棋盘上移动角色是一种相当通用的操作,可以独立于角色,除了一些信息(例如角色如何移动,是否允许他们移动等)应该是所说的一部分基类/接口。

在合理的情况下,独立设计类,以便它们可以自行测试,但不要以测试为理由编写糟糕的代码。可以创建简单的存根或基本测试实例来帮助进行组件测试,并且比修复不必要的复杂代码所花费的时间和精力要少得多。 接口是有目的的,但如果您不将 2 个类视为相同...不是这样。

*使用 MVC 也可以帮助您进行测试。如果操作正确,您应该能够交换任何层以简化对单个层的测试。

【讨论】:

  • “为什么只有这两个选项?”,我更新了问题,如果可行,我也可以接受另一个选项。
  • 在第二个选项中,我不会向下转换为不同的类型,而只是转换为属于模型的类型。这仅对公共方法参数是必需的(上层仅使用模型类的接口)。这样做的目的是通过特定于模型内部操作和模型内部操作所需的接口方法访问非发布。
  • 感谢您的回答,我只是想澄清一下:您认为我应该使用第二个选项,对吗?
  • 我认为两者都不正确。接口旨在允许将 2 个或更多类视为相同。例如。它强制它们之间的共性并允许通用使用。如果您只打算向下转换并使用派生类型,则代码不会获得任何东西。您仍然必须将派生类型包含到另一个类中,这不会避免一个类“了解”另一个类。您获得的只是泛型参数,但从它的声音来看,您并不打算使用泛型类型。
猜你喜欢
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
  • 2020-03-19
  • 2013-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
相关资源
最近更新 更多