10

我现在正在做的一件事与游戏有一些相似之处。出于说明的目的,我将使用一个虚构的假设游戏中的示例来解释我的问题。

让我们称之为DeathBlaster 4: The Deathening 吧。在 DB4 中,您有许多在移动时Ship会定期随机遇到的对象Phenomena。一个给定的在遇到它的时候Phenomenon可能有零个、一个或多个。例如,我们可能有四种和三种。EffectsShipShipsPhenomena

                              现象
              ===========================================
船舶重力井黑洞星云场
------------ -------------------------------------- ----
红船 +20% 速度 -50% 力量 -50% 护盾
BlueShip 无效果 无敌死亡 各种效果
绿船 -20% 速度死亡 +50% 护盾 船上现象
YellowShip 死亡 +50% 功率无效果    

此外,Effects还可以相互交互。例如,GreenShip同时在 aGravityWell和 a中的 a 可能会在生成的和NebulaField之间产生某种协同作用。在这种情况下,协同效应本身就是一种——例如,这种相互作用可能会产生一种。除了作用于 a的集合之外,不需要任何信息来解决最终结果应该是什么。SpeedEffectShieldEffectEffectPowerLevelSynergyEffectEffectsShip

您可能会开始看到这里出现的问题。作为一种天真的第一方法,要么每个Ship人都必须知道如何处理每个人Phenomenon,要么每个Phenomenon人都必须知道每个人Ship。这显然是不可接受的,因此我们想将这些责任转移到其他地方。显然这里至少有一个外部类,可能是一个MediatorVisitor某种。

但是最好的方法是什么?理想的解决方案可能具有以下属性:

  • Ship添加一个新的就像添加一个新的一样容易Phenomenon
  • 不产生影响的交互是默认的,不需要额外的代码来表示。约定优于配置。
  • 了解Effects彼此之间的交互方式,并能够管理这些交互以决定最终结果。

我想我已经决定了我的方法,但我很想听听最佳设计共识是什么。你会从哪里开始?你会探索哪些途径?



后续更新:谢谢大家的回复。这就是我最终要做的事情。我的主要观察是,相对于可能的×交互的数量,不同的数量Effects似乎很小。即,虽然有许多可能的交互组合,但这些交互的结果种类数量较少。PhenomenaShips

可以看到,比如表格中虽然有12种交互组合,但效果只有五种速度修改、力量修改、护盾修改、无敌、死亡。

我引入了第三个类 ,InteractionResolver来确定交互的结果。它包含一个映射Ship-Phenomenon对的字典Effects(基本上是一个执行效果的委托和一些元数据)。当计算交互的结果完成时,每一个都会Ship被传递EffectStack给它正在经历的对应。Effects

Ships然后使用EffectStack来确定它们的实际结果,方法是Effects向它们现有的属性和属性添加修饰符。

我喜欢这个,因为:

  • Ships 永远不需要知道Phenomena
  • Ship-关系的复杂性Phenomena被抽象为InteractionResolver.
  • 如何解决多种可能复杂的效果的细节由InteractionResolver. 船只只需在必要时应用效果。
  • 这可以进行其他有用的重构。例如,船舶处理效果的方式可以通过制作EffectProcessorStrategy. 默认可能是处理所有效果,但是,比如说,aBossShip可能会通过使用不同的EffectProcessorStrategy.
4

7 回答 7

1

一个有趣的潜在选择是使用访问者模式的变体。

Judith Bishop 和 R. Nigel Horspool 写了一篇关于设计模式效率的论文,他们在其中解释了使用 C# 3 特性的经典访问者模式的各种变体。

特别是,我将看看他们如何与代表一起处理访问者模式。使用委托列表或堆栈可能会为您提供一种有趣的方式来处理来自多个对象的多个效果,并且可以更轻松地扩展类层次结构的任一侧(添加船舶或添加效果),而无需大量破坏代码更改。

于 2009-03-26T00:14:25.493 回答
0

不完全是一个答案,但对我来说,这相当尖叫“属性模式”。对此有一个著名的 yegge rant,我认为它会为您提供一些不错的指导。

http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

于 2009-03-26T00:05:12.160 回答
0

这看起来像是经典的 OOP 多态性/访问者困境。但是,您的要求使其变得更容易。

基本上,我会创建一个Ship所有具体 Ships 派生自的基类。这个类将有方法:

class Ship
{
  void encounterBlackHole() {}
  void encounterNebula() {}
  ... etc. ...
};

具有空的默认主体。当你添加一个新的现象时,你只需添加一个空主体的新方法。(这些方法可能有参数,比如黑洞的坐标或权重等。)

对于效果及其交互 - 我认为您应该添加更多关于您想要它的信息,例如。交互是罕见的还是常见的,它们是否以某种方式累积,它们是否仅限于他们控制的有限变量集......

于 2009-03-26T00:12:23.207 回答
0

对我来说,这听起来像是一个经典的多重调度问题

于 2009-03-26T00:20:02.093 回答
0

有趣的问题

以某种方式,一艘船必须知道哪些现象会影响它,以及它对哪艘船有影响的现象。

这可以存储在运行时解析的 xml 文件中。

也许您可以使用装饰器模式来计算效果。您在运行时会产生各种现象。

假设您的船实现了一个接口 IShip,并且在您的代码中的任何地方,您都使用 IShip。

现在,假设您的所有现象也实现了接口 IShip(装饰器设计模式要求)。

IShip myShip = myShip.AddPhenomena(PhenomenaGeneratedByAFactoryForThisShip);

在现象中,您包装了原始船舶的方法,因此您可以对属性和所有内容进行修改。

此外,如果您使用策略模式,您可以生成任何您想要的现象。

移除一个现象,可以通过走你拥有的那堆装饰船,然后重新包装它来使用,所以我认为那里没有问题。

至于协同作用,我想我会使用稍微修改过的现象,这只有在目标船本身拥有所有现象时才有效。

于 2009-03-26T00:22:59.013 回答
0

要么每艘船都必须知道如何处理每一种现象,或者每条现象都必须了解每艘船。

我认为这是解决您问题的关键,如果每个船舶现象交互都是独一无二的,那么这是真的。在您创建的表中,情况似乎如此,因此对于 n 艘船和 m 个现象,您需要指定 n*m 交互。你闻到维护问题是对的。

唯一的出路是通过使现象的影响取决于船舶的属性,从而使船舶与现象的相互作用不再那么独特。例如,我们可以说只有用奇怪氧化铝建造的船才能在黑洞中幸存下来。

但是只有一处房产并不能给你带来任何好处,你可以很容易地确定哪些船只受到黑洞的影响。关键是多个属性表现出相同的组合爆炸。

因此,用奇怪氧化铝建造的船将在黑洞中幸存下来,而没有用奇怪氧化铝建造的船可以走得更快。1 属性允许您指定 2 件事(1 位 = 2 种可能性)。用 Corbite 发动机建造的船只在曲速区会更快。同时具有碳酸盐发动机和奇异氧化铝的船舶将在星云场等中获得 50% 的护盾。

向船添加属性可以避免指定每种现象如何与每艘船相互作用,但仍然让每种现象和每艘船都表现出适当的行为。

如果有 M 艘船,那么您只需要 log2(M) 属性即可为每艘船提供独特的行为。

于 2009-03-26T00:41:55.303 回答
0

我认为问题的答案取决于问题的好坏。

我认为设计的方式取决于问题是什么(或者问题将在未来出现)

你给一个表,那么我认为解决方案是维护一个表并查询它。

此处的python代码:(未经测试,仅显示示例)

class Ship():
     def __init__(self,type):
         self.type=type
     def encounterPhenomena(self,type): # let Phenomena to process ship
         p = Phenomena(type)
         p.process(self)

class Phenomena():
     processTable = {}
     def __init__(self,type):
         self.type=type
     def process(self,ship):
         try:
             self.processTable[self.type](ship.type) #query the internal table to process ship
         except:
             pass #if Phenomena don't know this ship type then no process..
     def addType(type,processMethod):
         processTable[type]=processMethod #add new Phenomena, and add processMethod

def run():
    A = Ship(type = 'RedShip')
    A.encounterPhenomena(type='GravityWell');

如果处理方法发生了变化,只需修改 Phenomena 类中的处理方法即可。

如果您认为船舶需要知道如何处理 Phenomena,则将处理方法更改为船舶类。

或者您认为除了 Phenomena 需要更改船舶状态(如其他船舶、浅滩岩石)之外还有其他事情,您需要在船级中维护一个进程表并使 Phenomena 成为其中之一,

再说了,怎么设计,就看自己的问题了。

于 2009-03-27T03:34:17.923 回答