【发布时间】:2015-07-06 08:30:21
【问题描述】:
我在使用 C# 制作的游戏中遇到了问题。这是一个简单的基于图块的匹配游戏,而我正在尝试制作的电源出现问题:
假设我们有基本的瓷砖类型,圆形、正方形和菱形,它们都是 Tile 的子类。我尝试将“匹配”行为提取到抽象的 Tile 方法:canMatchWith(Tile t),而不是让圆圈只匹配圆圈。 Tiles 也有两种方法来添加/删除它们可以匹配的 Tiles。
假设我们在游戏中间有一个圆形图块,并且我们有一个通电,上面写着“圆形图块可以在本回合与正方形图块匹配”。我会遍历所有圆形图块并说 circleTile.addCanMatchWith(typeof(Square))。在内部,我们有一个 List canMatchWith。
然后,我想说“圆不能再与正方形匹配”,简单地说 circleTile.removeCanMatchWith(typeOf(Square))。
这是我目前的解决方案,它运行良好,没有我注意到的性能缺陷(这是一个基于图块的匹配游戏,因此这些类型每次“移动”只评估一次,而不是逐帧评估)。但是,我脑海中的声音告诉我,这是完成此行为的不好方法。所以我有一些选择:
- 枚举... 每个 Tile 可以由 Tiletype 类型变量组成。这将在构造函数中初始化并设置为 Type.SQUARE 用于正方形,依此类推。然后,每个 Tile 都会有一个 List canMatchWith,功能和我原来的实现是一样的。除了在这种情况下,它有点棘手。假设我有一些圆形子类,椭圆形和椭圆形。我希望椭圆只能匹配正方形,但椭圆可以匹配所有圆圈而不是正方形。
这里的问题是冗余,我的枚举现在也将具有 OVAL 和 ELIPSE,并且 Elipse 类将具有 (CIRCLE, OVAL, ELIPSE TileTypes) 作为它可以匹配的类型。这完全是多余的,我只想说“圆圈”,我可以用这些类型。我想 Tiles 可能有 TileType baseType 和 TileType actualType。
- 某种形式的行为组合。忘记 Tile 子类,只需给 Tiles 方法和 List 的实例变量。然后,在运行时我们可以说 someTile.addCanMatch(new CircleMatchBehavior())。这似乎很愚蠢,因为我会有一堆课程只是说你可以匹配特定的形状。
总之,我想要完成的是让多个对象类型能够与任意数量的不同类型进行交互。问题是,我应该为 Type 使用什么。这里可以使用 GetType 吗?枚举?还是有人会推荐更好的策略?我试图尽可能笼统,这些图块不应该对其他图块有任何硬编码依赖,并且必须能够随时更改它们可以与之交互的对象。假设我创建了一个新的 Tile 子类 pentagon... 嗯,Pentagons 可以与 Squares、Circles 和 Pentagons 匹配。我的实现很容易,但有些事情告诉我这是一种肮脏的 OOP 实践。
我觉得我必须使用类型/枚举,因为我不想说 thisTile.addCanMatch(Tile someOtherObject)。这太具体了,我希望 thisTile 能够与作为特定类实例的所有图块匹配。
【问题讨论】:
-
恐怕我不是一个足够熟练的设计师,无法为您推荐一个完整的设计,但如果圆形和椭圆形的方式没有实际的功能差异操作,最好让所有实例都是
Tile类,然后设置某种Behavior属性。棋盘上的所有圈子可能都可以共享同一个Behavior对象,然后当规则更改一回合时,您可以在该Behavior对象上设置更改。如果我了解情况,这可能会减少代码重复。 -
如果我的理解正确,这有点像我的#2 行为组合。我在这里担心的是行为爆炸。例如,MatchesWithCirclesAndSquaresBehavior。看看我在说什么?如果有 5 种类型的 Tiles,那么我们有 5 选 1 加 5 选 2 加 5 选 3 加 5 选 4 加 5 选 5 的行为组合。这似乎比 GetType 更糟糕。但是,我可能会误解您的建议中的某些内容。 Tiles 自己做任何他们想做的事情,一个人可能会做一个爆炸动画等。这仅适用于 tile 可以与另一个匹配的情况。
-
我并不是建议 Behavior 是一个枚举或一个不可修改的类。您可以通过多种方式对其进行子类化,但主要我只是希望它有一个内部集合,表示它的类型(Circle)和它可以匹配的类型(Square)。然后,您可以以某种面向数据的方式进行更改。我绝对同意应该避免任何会导致大量 if/else/switch 块的事情,以支持良好的对象设置,但我们也希望避免编写新类来解释一些不太新的行为变化。
标签: c# oop object reflection instanceof