代码之家  ›  专栏  ›  技术社区  ›  John Feminella

解决交互组合爆炸的最佳方法是什么?

  •  10
  • John Feminella  · 技术社区  · 17 年前

    我现在正在做的一件事与游戏有一些相似之处。为了便于说明,我将使用一个从虚拟的、假设的游戏中提取的示例来解释我的问题。

    让我们称之为 死亡冲击波4:死亡 . 在DB4中,您有一个 Ship 定期和随机遇到的物体 Phenomena 当他们旅行时。给定的 Phenomenon 可能有零个、一个或多个 Effects 在一 遇到了。例如,我们可能有四种 Ships 还有三种 现象 .

                                  Phenomena
                  ==========================================
    Ships         GravityWell     BlackHole      NebulaField
    ------------  ------------------------------------------
    RedShip       +20% speed      -50% power     -50% shield
    BlueShip      no effect       invulnerable   death              Effects of Various
    GreenShip     -20% speed      death          +50% shield        Phenomena on Ships
    YellowShip    death           +50% power     no effect    
    
    

    此外, 影响 可能相互影响。例如,A GreenShip 两者都有 GravityWell 和A NebulaField 可能在生成的 SpeedEffect ShieldEffect . 在这种情况下,协同效应本身就是 Effect --例如,可能有 PowerLevelSynergyEffect 这是这种相互作用的结果。除了一组 影响 扮演一个 需要解决最终结果是什么。

    你可能会发现这里出现了一个问题。作为一种幼稚的第一种方法, 要么每一个 必须知道如何处理 现象 ,或每一个 现象 必须了解 .这显然是不可接受的,所以我们想把这些责任转移到别处。显然,这里至少有一个外部类,可能是 Mediator Visitor 在某种程度上。

    但最好的方法是什么?理想溶液可能具有以下性质:

    • 很容易添加新的 因为它是要添加一个新的 现象 .
    • 不产生任何效果的交互是默认的,不需要额外的代码来表示。配置上的约定。
    • 了解如何 影响 相互影响,并且能够管理这些交互以决定最终结果是什么。

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



    后续更新: 谢谢你们的回复,各位。这就是我最后要做的。我的主要观察是 影响 相对于可能的数量来说似乎很小 现象 × 船舶 相互作用。也就是说,尽管有很多可能 组合 互动的数量 结果的种类 这些交互作用中有一个较小的数字。

    例如,您可以看到,尽管表中有12个交互组合,但只有5个 种类 影响:速度的改变,力量的改变,盾牌的改变,无懈可击,死亡。

    我介绍了第三节课 InteractionResolver ,以确定交互的结果。它包含一本地图字典 Ship-Phenomenon 配对对 影响 (基本上是执行效果的委托和一些元数据)。各 被交给 EffectStack 对应于 影响 当计算交互的结果完成时,它就会体验到。

    船舶 然后使用 效应叠加 以确定 影响 在它们的现有属性和属性中添加修改器。

    我喜欢这样是因为:

    1. 船只从不需要了解现象。
      • 将船舶现象关系的复杂性抽象为交互性分解器。
      • 如何解决多种可能的复杂效应的细节被 交互冲突解决程序 . 船舶只需在必要时应用效果。
      • 这将启用其他有用的重构。例如, 方式 在这种情况下,船舶过程的影响可以通过 EffectProcessorStrategy . 默认值可能是处理所有效果,但是,例如, BossShip 可能会因为有不同的 有效流程或策略 .
    7 回复  |  直到 17 年前
        1
  •  1
  •   Reed Copsey    17 年前

    一个有趣的潜在选择是使用 Visitor Pattern .

    朱迪丝·毕晓普和R·奈杰尔·霍斯波写了一篇关于 design pattern efficiency 其中,他们使用C 3功能解释了传统访客模式的各种变体。

    特别是,我将了解他们如何与代表一起处理访客模式。使用一个委托列表或一堆委托可能会给您提供一种有趣的方法来处理来自多个对象的多个效果,并且更容易扩展类层次结构的任一侧(添加船舶或添加效果),而不会发生巨大的中断代码更改。

        2
  •  0
  •   Lirik    17 年前

    不完全是个答案,但对我来说,这简直就是“属性模式”。有一个著名的耶格咆哮关于这,我想它会给你一些体面的指针。

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

        3
  •  0
  •   jpalecek    17 年前

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

    基本上,我会创建一个基类 Ship 所有的混凝土船都是从那里来的。此类将具有以下方法:

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

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

    对于效果和它们的相互作用-我认为你应该添加更多关于你想要它的信息,例如,这些相互作用是罕见的还是常见的,它们是以某种方式累积的,它们是否局限于它们控制的一组有限的变量…

        4
  •  0
  •   user60401    17 年前

    听起来很经典 multiple dispatch problem 对我来说。

        5
  •  0
  •   Martin    17 年前

    有趣的问题

    在某种程度上,一艘船必须知道什么现象会影响它,什么现象会影响它在哪艘船上。

    这可以存储在运行时分析的XML文件中。

    也许你可以用 Decorator pattern 计算效果。在运行时产生各种现象。

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

    现在,假设您的所有现象也实现了接口iship(decorator设计模式所需)。

    IShip myShip = myShip.AddPhenomena(PhenomenaGeneratedByAFactoryForThisShip);
    

    在这种现象中,您将方法从原始船包装起来,这样您就可以对属性和所有属性进行修改。

    另外,如果您使用 strategy pattern 你可以产生任何你想要的现象。

    除去一个现象,可以用你走过的那堆装饰过的船,并重新拍打它,所以我看不出有什么问题。

    至于协同效应,我想我会使用一个稍微修改过的现象,它只有在目标船自身有所有的现象时才起作用。

        6
  •  0
  •   Paul    17 年前

    每艘船都必须知道 如何处理每一种现象,或 每个现象都必须知道 关于每艘船。

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

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

    但是只有一处房产并不能为你买任何东西,你可以很容易地指定哪些船会受到黑洞的影响。关键是多个性质表现出相同的组合爆炸。

    因此,用奇怪的氧化铝建造的船只可以在黑洞中生存,而不用氧化铝建造的船只可以走得更快。1属性允许您指定2件事(1位=2种可能性)。用Corbite引擎建造的船在弯曲区会走得更快。同时拥有Corbite引擎和奇怪氧化铝的飞船将在星云领域获得50%的护盾,等等。

    将属性添加到船舶可以避免指定每个现象如何与每艘船舶交互,但仍然有每个现象,并且每艘船舶都显示出适当的行为。

    如果有m条船,那么您只需要log2(m)属性就可以为每个船提供一个独特的行为。

        7
  •  0
  •   linjunhalida    17 年前

    我认为问题的答案取决于问题问得有多好。

    我认为设计的方法取决于问题是什么(或者问题将来会发生)。

    给出一个表,然后我认为解决方案是维护一个表并查询它。

    这里是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');
    

    如果过程方法发生更改,只需在现象类中修改过程方法。

    如果你认为船需要知道如何处理现象,那么就把处理方法改为船类。

    或者你认为还有其他的事情,不仅是需要改变船的状态的现象(像其他的船,浅滩岩石)。 您需要在Ship类中维护一个流程表,并使现象成为其中之一,

    再说一遍,如何设计取决于问题本身。

    推荐文章