Шаблон разработки стратегии - выбор стратегий со счетчиками

Я программирую на Java, но это скорее вопрос дизайна, поэтому любой программист OO мог бы ответить на этот вопрос. У меня есть вопрос относительно шаблона проектирования Стратегии. Вот несколько чернил, которые я нашел полезными:

  1. Разработана стратегия шаблона-OO.

Я использую шаблон стратегии дважды с одной группой из четырех стратегий и одной группой из трех. В каждом случае я решаю, какую стратегию использовать, поддерживая затухающий счетчик. если стратегия, которую программное обеспечение решит использовать, успешна, то счетчик увеличивается на единицу. Если используемая стратегия не выполняется, счетчик уменьшается на единицу. Независимо от успеха или неудачи. ВСЕ счетчики умножаются на число около 0,9, чтобы "разложить" счетчики с течением времени. Программное обеспечение будет выбирать, какую стратегию использовать на основе того, какая стратегия имеет самый высокий счетчик. Пример моего очень простого UML показан ниже:

Example UML.

И в форме ссылки (для удобства чтения): Пример UML

UML выше - макет, который я бы хотел использовать. Если вы не можете сказать из вышеупомянутого UML, я пишу игру Rock, Paper, Scissors с намерением избить всех своих друзей.

Теперь, к проблеме:

Я не могу решить, как реализовать "систему счетчиков" для принятия решения о том, какую стратегию использовать. Я думал о каком-то классе "данных", где все счетчики и строки истории могли быть сохранены, но это просто показалось мне неуклюжим. Во все времена я поддерживаю около 2 строк и около восьми счетчиков (возможно, может быть, меньше). Вот почему я думал о классе "данных", где все могло быть сохранено. Я мог бы просто создать экземпляр класса, который будет использоваться в методах selectStrategy() и selectMetaStrategy(), но я просто не знаю. Это мой первый проект, над которым я буду работать самостоятельно, и я просто ничего не могу решить. Я чувствую, что есть определенно лучшее решение, но я недостаточно осведомлен об этом.

Спасибо!

------------------------------------ последующие действия 1 ------- -------------------------------------

Большое вам спасибо за ответы и добрые слова. Однако у меня есть несколько вопросов. Я новичок в StackOverflow (и люблю его), поэтому, если это не правильное место для последующего вопроса, пожалуйста, дайте мне знать. Я редактирую свой оригинальный пост, потому что мое наблюдение немного длительное.

Я смотрел совет Пола Сонье об использовании сложного рисунка, и это выглядело очень интересно (спасибо Пол!). Для целей StrategyMatching и "интеллектуальных" стратегий AntiRotation я хочу реализовать цепочку всех игр противников, доступных для обоих классов. Кроме того, я хочу, чтобы строка истории редактировалась независимо от того, какую стратегию играла моя программа, чтобы я мог вести точную запись оппонента. Более полная строка (на самом деле я, вероятно, буду использовать LinkedList, но если кто-нибудь знает о лучшем методе/коллекции поиска (sub-String/sub-List), пожалуйста, дайте мне знать), тем лучше стратегия может предсказать поведение противника.

Мне было интересно, как я мог бы реализовать эту "строку" или коллекцию, все еще используя составной шаблон.

Кроме того, TheCapn поднял, что было бы неплохо хранить разные счетчики и коллекции истории для каждого противника. Любая мысль о том, как реализовать это с помощью составного шаблона?

Ответ 1

В идеале, цель состоит в том, чтобы счетчики были связаны со стратегиями, потому что они рассчитывают на успех стратегий. Тем не менее, вы не обязательно хотите, чтобы стратегии знали что-либо о том, что они подсчитываются. Для меня это указывает на композитный шаблон, посредством которого вы завершаете свой класс Strategy в классе, который имеет логику отслеживания/унижения/изменения количества использований.

Это дает вам местность (счет хранится со стратегией, которую он подсчитывает) и функциональный состав (функция подсчета инкапсулируется в классе композиции). Кроме того, он поддерживает выделение класса стратегии из других влияний.

Ваша разбивка по дизайну пока выглядит хорошо; вы, безусловно, на хорошем и интересном пути. Надеюсь, это поможет!

Ответ 2

Во-первых, я предлагаю вам попробовать что-то более простое: перемещение вперед. Держите свои стратегии в списке, изначально в произвольном порядке. Проделайте их так:

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. Поместите их в список. Когда вам нужна стратегия, работайте над списком, вызывая каждую стратегию, пока не нажмете тот, который работает. Затем просто сортировать список. Затем стратегии будут в порядке, от лучшего к худшему.

Ответ 3

Хотя "неуклюжий", я думаю, ваша интуиция верна. Сохраните все data в collection, к которым можно получить доступ по стратегии, которую вы решили реализовать. Сохраняйте отдельные объекты data для каждого из ваших оппонентов и создавайте новые объекты data при определении новых противников, сохраняя их в коллекции, такой как Map, обеспечит легкую доступность.

Причина этого в том, что если/когда вы решите изменить "MetaStrategies", вам понадобится релевантная информация, доступная для ваших объектов, путем хранения их в других абстрактных объектах вы можете найти поиск/разбор/сбор данных больше трудно, тогда это должно быть. Это также близко соответствует ментальной модели, которую вы создали для себя, поэтому попытка идти против этого потока может привести к ошибкам дизайна.

Единственное другое логическое из этого (из моего краткого мозгового штурма) - это лучше определить окружающую логику или эвристику, которую вы планируете реализовать. Если вы создадите более конкретный способ выбора MetaStrategy, у вас будет лучшее понимание того, как данные должны собираться для доступа. Метод Том Андерсон выглядит многообещающим для вашего проекта!