【问题标题】:Queueing Methods With Java 8Java 8 中的队列方法
【发布时间】:2023-04-09 05:35:01
【问题描述】:

我正在构建一个游戏模拟器,它有数百个微步骤,如下所示。它们各自执行一项独特的任务,但为了简洁起见,我省略了实现细节。

public class Sim {

    static void phase() {
        phaseIn();
        phaseOut();
    }

    static void untap() {
    }

    static void upkeep() {
    }

    static void draw() {
    }

    ...
}

Turn 通常涉及顺序执行步骤,但有时特殊效果可能会导致顺序发生变化。例如,我可能需要重复一个步骤两次、跳过一个步骤或重新排列步骤的顺序。这些动作都是特殊情况,因为转弯通常只是从开始到结束的顺序发生。

例如,下面的一系列事件代表我的正常轮到。

... > upkeep() > draw() > preCombatMain() > ...

现在,我玩的东西需要我重复抽牌步骤。我需要变成这样:

... > upkeep() > draw() > draw() > preCombatMain() > ...

一个回合的步骤是方法,我不知道如何使方法入队或出队。我知道 Java 8 有方法引用,但该功能相对较新。我无法将existing tutorials 应用于我想要完成的工作。我到了Sim::untap,但我不知道如何分配、调用它等。如何在 Java 8 中对方法进行排队,或者按照运行时确定的顺序调用方法- 时间取决于玩家做出的选择?

注意:我意识到我无法理解可能是由于基本的设计缺陷。我从未上过游戏设计课程,我完全愿意接受批评,如果设计有缺陷,我会改变我的设计。也就是说,这个问题不应被误解为“请推荐一种设计模式”。我考虑过另一种设计,我在大量 switch 语句中为每个步骤“编号”,排队“编号”,并在队列的前面反复切换,但这似乎是一个糟糕的计划(在我看来)。

【问题讨论】:

  • 您为什么要使用 Java 8 开发游戏?如果您打算分发它,则需要一段时间才能被市场采用。 Java 7 是多年前发布的,但仍然不是标准。如果您使用 Java 8 的特定功能,您可能会错过部分市场。
  • @DeadChex 这甚至不是可分发的产品。如果我这样做,我将违反版权法。我编写游戏是为了好玩,并提高我对规则的理解,这些规则相当全面。如果我碰巧完成了它,那么我和我的朋友们就可以免费练习了。
  • @DeadChex 最坏的情况是你还是捆绑了 JRE。
  • 如果只是排队方法调用的问题,你可以只使用Queue<Runnable>并将每个步骤实现为实现Runnable的类。但我怀疑一个简单的基于队列的设计不够灵活,无法处理游戏中可能发生的所有事情,而且隐含的“真实”问题,即如何在 Java 中实现 M:TG 可能对StackOverflow 问题。
  • @TomG JRE8 可以在当今大多数平台上运行吗?

标签: java java-8


【解决方案1】:

如果你只是想让它们按顺序运行,你当然可以一个接一个地调用它们。如果顺序可以更改,另一种方法是使用方法引用队列:

LinkedList<Runnable> queue = new LinkedList<>();
queue.add(Sim::upkeep);
queue.add(Sim::draw);
queue.add(Sim::preCombatMain);
queue.forEach(Runnable::run);

我能够使用LinkedList&lt;Runnable&gt;,因为您的方法的签名是void m()。对于其他签名,您可以使用其他类型,例如:

  • void m() 使用 Runnable
  • T m() 使用 Supplier&lt;T&gt;
  • void m(T o) 使用 Consumer&lt;T&gt;
  • R m(T o) 使用 Function&lt;T, R&gt;

【讨论】:

  • 抱歉不清楚。 “队列方法”,我的意思是“将代码块放在先进先出的结构中,例如队列”。不过,我认为您即使在不理解的情况下也回答了这个问题。如果您可以将方法放入List,如您所演示的,那么将它们放入Queue 将是微不足道的。如果我让它在队列而不是列表中工作,我会回来编辑你的问题(如果你不介意的话),并奖励你答案。
  • @John 这个答案没有将 methods 放在列表中,而是将 call 方法的 Runnables 放在列表中。是的,您可以通过队列来完成。
  • LinkedList 实现Queue。 :)
  • @DavidConrad 这个答案是在我评论后编辑的,所以它现在使用的是 LinkedList 而不是 List。我选择不删除我原来的评论以免radiodef的评论失效。
  • @John 请注意,代码原样不使用列表的队列特征,因为它只应用了forEach。因此,ArrayList 的行为方式完全相同(这是我在您编辑之前在原始答案中使用的方式)。如果您需要排队/出队,那么显然它可能是有意义的。
【解决方案2】:

解决方案是使用多态性。为步骤定义一个接口:

interface Step {
  void process();
}

然后通过实现来定义每个步骤:

class UpkeepStep implements Step {
  void process() { ... }
}

现在您可以将所有步骤放入一个数组中,根据需要对其进行随机播放,然后执行所有步骤,如下所示:

for (Step step : steps) {
  step.process();
}

另一种可能运行得更快的方法是生成包含方法调用的代码,对其进行编译并加载类。但是,如果与方法调用开销相比,该步骤不占用太多运行时间,并且如果您多次执行每段生成的代码,那么它只会为您提供更好的性能,因此 JIT 会对其进行优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 2011-12-01
    • 1970-01-01
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多