策略设计模式 - 在计数器策略之间进行选择

时间:2011-07-07 16:56:09

标签: java oop design-patterns strategy-pattern

我使用Java编程,但这是一个设计问题,因此任何OO程序员都可以回答这个问题。我对战略设计模式有疑问。以下是我发现有用的几种墨水:

  1. Strategy Pattern Explained-OO Design
  2. 我使用策略模式两次,一组四个策略,一组三个。在每种情况下,我都通过维持一个腐朽的计数器来决定使用哪种策略。如果软件决定使用的策略成功,则计数器增加1。如果使用的策略不成功,则计数器减1。无论成功与否,所有计数器都会乘以.9左右的数字,以便随着时间的推移“衰减”计数器。软件将根据哪个策略具有最高计数器来选择使用哪种策略。我非常简单的UML的一个例子如下所示:

    Example UML

    以链接形式(为了便于阅读): Example UML

    上面的UML是我想要使用的模型。如果你从上面的UML中无法分辨,我正在写一个Rock,Paper,Scissors游戏,目的是击败我所有的朋友。

    现在,关于问题:

    我无法决定如何实施“计数器系统”来决定使用哪种策略。我在考虑某种“数据”类,其中可以存储所有计数器和历史字符串,但这对我来说似乎很笨拙。在任何时候我都维持着大约2个字符串和大约8个计数器(可能更多可能更少)。这就是为什么我在想一个可以存储所有东西的“数据”类。我可以实例化在chooseStrategy()和chooseMetaStrategy()方法中使用的类,但我只是不知道。这是我自己的第一个项目,我无法决定任何事情。我觉得肯定有更好的解决方案,但我没有足够的经验知道。

    谢谢!

    ------------------------------------后续1 ------- -------------------------------------

    非常感谢大家的回答和客气话。我确实有一些跟进问题。我是StackOverflow的新手(也喜欢它)所以如果这不是一个后续问题的正确位置,请告诉我。我正在编辑原帖,因为我的后续工作有点冗长。

    我正在研究Paul Sonier关于使用复合图案的建议,它看起来非常有趣(感谢Paul!)。出于HistoryMatching和“智能”AntiRotation策略的目的,我想实现两个类都可访问的所有对手游戏的字符串。此外,无论我的节目采用何种策略,我都希望编辑历史字符串,以便我能够准确记录对手的比赛。字符串越全面(实际上我可能会使用LinkedList但是如果有人知道更好的(子字符串/子列表)搜索方法/集合请让我知道)策略可以更好地预测对手的行为。

    我想知道如何在仍然使用复合模式的同时实现这个“字符串”或集合。

    此外,TheCapn提出,为每个对手存储不同的计数器和历史收集是个好主意。有关如何使用复合模式实现这一点的想法吗?

3 个答案:

答案 0 :(得分:6)

理想情况下,目的是让计数器与策略相关联,因为它们计算策略的成功。但是,您并不一定希望策略能够了解它们被计算在内的事实。对我来说,这表示一个Composite模式,您可以将Strategy类包装在一个类中,该类具有跟踪/降级/修改用法计数的逻辑。

这为您提供了位置(计数与其计算的策略一起存储)和功能组合(计数功能封装在组合类中)。同样,它保持了策略类与其他影响的隔离。

到目前为止,您的设计细分看起来不错;你当然是在一条美好而有趣的道路上。希望这有帮助!

答案 1 :(得分:3)

首先,我建议你尝试一些更简单的事情:move-to-front。最初以任意顺序将策略保存在列表中。像这样通过它们:

List<Strategy> strategies;
Strategy successfulStrategy = null;
for (Strategy strategy: strategies) {
    boolean success = strategy.attempt();
    if (success) {
        break;
        successfulStrategy = strategy;
    }
}
if (successfulStrategy == null) throw new NoSuccessfulStrategyException();
// now move the successful strategy to the front of the list
strategies.remove(successfulStrategy);
strategies.add(0, successfulStrategy);

基本上,一个成功的策略直接进入队列的头部;随着时间的推移,良好的策略积累在头部附近。它并不像基于计数的东西那样微妙,但它很简单,而且在实践中,对于各种用途,它的效果非常好。

但是,如果你没有设置计数,那么我要做的是创建一个Decorator,它包装策略并保持计数,并且可以与其他此类对象进行比较。代码是最简单的解释:

public class ScoredStrategy implements Strategy, Comparable<ScoredStrategy> {
    private final Strategy delegate;
    private float score;

    public ScoredStrategy(Strategy delegate) {
        this.delegate = delegate;
    }

    public boolean attempt() {
        boolean success = delegate.attempt();
        score = (score * 0.9f) + (success ? 1 : -1);
        return success;
    }

    public int compareTo(ScoredStrategy that) {
        return -Float.compare(this.score, that.score);
    }
}

在主要对象中,采用您的实际策略,并将每个策略包装在ScoredStrategy中。把它们放在一个清单中。当您需要策略时,请在列表上工作,调用每个策略,直到找到有效的策略。然后,只需sort the list。从最佳到最差,策略将按顺序排列。

答案 2 :(得分:1)

虽然“笨重”但我认为你的直觉是正确的。将所有data存储在可由您选择实施的策略访问的collection中。为每个对手保留单独的data个对象,并在定义新对手时创建新的data个对象,将其存储在Map等集合中将提供方便的可访问性。

这背后的原因是,如果/当您决定更改“MetaStrategies”时,您将需要对象可访问的相关信息,通过将它们存储在其他抽象对象中,您可能会发现搜索/解析/收集数据更多它应该是困难的。这也与您为自己生成的心理模型非常匹配,因此试图违背该流程可能会导致设计错误。

围绕这个(从我简短的头脑风暴)的唯一其他逻辑是更好地定义您计划实施的周围逻辑或启发式。如果您创建一种更具体的方法来选择MetaStrategy,您将更好地了解如何收集数据以进行访问。汤姆安德森的方法看起来很有希望你的项目!