开发者

Strategy Design Pattern- choosing between strategies with counters

开发者 https://www.devze.com 2023-03-18 05:52 出处:网络
I am programming in Java but this is a more of a design question so any OO programmer could probably answer this question.I have a question concerning the Strategy design pattern.Here are several inks

I am programming in Java but this is a more of a design question so any OO programmer could probably answer this question. I have a question concerning the Strategy design pattern. Here are several inks I have found usef开发者_StackOverflow社区ul:

  1. Strategy Pattern Explained-OO Design.

I am using the strategy pattern twice with one group of four strategies and one group of three. In each case I am deciding which strategy to use by maintaining a decaying counter. if the strategy the software decides to use is successful then the counter is incremented by one. If the strategy used is not successful then the counter is decremented by one. Regardless of success or failure ALL counters are multiplied by a number around .9 to "decay" the counters over time. The software will choose which strategy to use based on which strategy has the highest counter. An example of my very simple UML is shown below:

Strategy Design Pattern- choosing between strategies with counters

.

And in Link form (for easier reading): Example UML

The UML above is the mockup I would like to use. If you can't tell from the above UML, I am writing a Rock, Paper, Scissors game with the intention of beating all of my friends.

Now, on to the problem:

I cannot decide how to implement the "counter system" for deciding which strategy to use. I was thinking about some kind of "data" class where all counters and history strings could be stored, but that just seemed clunky to me. At all times I am maintaining about 2 strings and about eight counters (maybe more maybe less). That is why I was thinking about a "data" class where everything could be stored. I could just instantiate the class to be used in the chooseStrategy() and chooseMetaStrategy() methods, but I just don't know. This is my first project that I will be working on my own for and I just cannot decide on anything. I feel like there is definitely a better solution but I am not experienced enough to know.

Thanks!

------------------------------------follow-up 1--------------------------------------------

Thank you so very much on everyone's answers and kind words. I do have a few follow up questions though. I am new to StackOverflow (and loving it) so if this is not the correct place for a followup question please let me know. I am editing my original post because my follow-up is a little lengthy.

I was looking into Paul Sonier's advice about using the composite pattern and it looked very interesting (thanks Paul!). For the purpose of the HistoryMatching and "intelligent" AntiRotation strategies I want to implement a string of all opponent plays accessible to both classes. Also, I want the history string to be edited no matter what strategy my program played so that I can keep an accurate record of the opponent's plays. The more comprehensive the string (actually I will probably use a LinkedList but if anyone knows of a better (sub-String/sub-List) search method/collection please let me know) the better the strategy can predict the opponent's behavior.

I was wondering how I could implement this "string" or collection while still using the composite pattern.

Also, TheCapn brought up that it would be a good idea to store different counters and history collections for each opponent. Any thought on how to implement this with the composite pattern?


Ideally, the intent is to have the counters be associated with the strategies, because they're counting the successes of the strategies. However, you don't necessarily want the strategies to know anything about the fact that they're being counted. To me, this indicates a Composite pattern, whereby you wrap your Strategy class in a class which has logic for tracking / degrading / modifying the count of usages.

This gives you locality (the count is stored with the strategy it's counting) and functional composition (count functionality is encapsulated in the composition class). As well, it maintains the isolation of the strategy class from other influences.

Your design breakdown so far looks good; you're certainly on a good and interesting path. Hope this helps!


Firstly, i'd suggest you try something a bit simpler: move-to-front. Keep your strategies in a list, initially in an arbitrary order. Work through them like this:

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);

Basically, a successful strategy moves straight to the head of the queue; over time, good strategies accumulate near the head. It's not as subtle as something based on counts, but it's simple, and in practice, for all sorts of uses, it works very well.

However, if you're dead set on a count, then what i'd do is create a Decorator which wraps a strategy and keeps a count, and which can be compared with other such objects. Code is the easiest explanation:

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);
    }
}

In the main object, take your actual strategies, and wrap each in a ScoredStrategy. Put those in a list. When you need a strategy, work over the list, calling each strategy until you hit one that works. Then, simply sort the list. The strategies will then be in order, from best to worst.


Although "clunky" I think your intuition is correct. Store all data in a collection that can be accessed by the strategy you choose to implement. Keep separate data objects for each of your opponents and create new data objects when you define new opponents, storing them in a collection such as a Map will provide easy accessability.

The reason behind this is if/when you decide to change "MetaStrategies" you'll need the relevent information accessible to your objects, by storing them in other, abstract objects you may find the searching/parsing/gathering of data more difficult then it should be. This also closely matches the mental model you've generated for yourself so attempting to go against that flow will possibly lead to design mistakes.

The only other logical away around this (from my brief brainstorming) is to better define the surrounding logic or heuristics you plan to implement. If you create a more concrete way of picking a MetaStrategy you'll have a better understanding of how the data needs to be collected for access. Tom Anderson's method looks promising for your project!

0

精彩评论

暂无评论...
验证码 换一张
取 消