OO Design, открытый / закрытый принципиальный вопрос

Я уже некоторое время размышляю над этим вопросом об объектно-ориентированном дизайне и не могу найти удовлетворительного решения, поэтому подумал, что брошу его здесь толпам, чтобы высказать свое мнение.

У меня есть класс Игра, представляющий пошаговую настольную игру, мы можем предположить, что для целей этого вопроса он похож на «Монополию». В моем дизайне у меня есть класс Игрок, содержащий метод Принять поворот.

Игра проходит через все Игрок и вызывает метод TakeTurn, чтобы сделать все необходимое для завершения поворота. Я хочу иметь возможность иметь n игроков и иметь возможность назначить произвольное их количество компьютерными игроками. Итак, я подумал о том, чтобы иметь класс HumanPlayer и класс КомпьютерПлеер, оба из которых являются производными от Player.

Игра знает только класс Игрок и просто вызывает метод Принять поворот для каждого Игрок по очереди. Моя проблема заключается в том, что объекты КомпьютерПлеер могут быть полностью автоматизированы, то есть в соответствии с примером «Монополия» могут принять решение о покупке собственности, используя некоторую логику. Теперь, с объектом HumanPlayer, он должен получить ввод от фактического пользователя, чтобы иметь возможность, например, купить собственность, что, похоже, подразумевает другой интерфейс и потенциально означает, что они не должны получать

Мне не удалось найти хорошее решение проблемы, если класс Игра не знает фактических реализаций различных классов Игрок. Я всегда мог сделать предположение в классе Игра, что всегда будут только люди и компьютерные игроки, и эффективно закрыть его для расширения, но это не похоже на хорошее объектно-ориентированное программирование.

Приветствуются любые мнения по этому поводу.

Я не уверен, что здесь применяется принцип открыт-закрыт.

Fuhrmanator 18.04.2012 16:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
1
909
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

Я думаю, вы не должны позволять классу Game обрабатывать ввод-вывод. таким образом (блокирующий) метод TakeTurn скроет с игрового поля средства реализации. он может использовать другие объекты для связи с пользователем.

Все, о чем должен заботиться класс Game, - это состояние доски и ход. все игроки должны реализовать интерфейс для одного игрока и скрыть всю реализацию от игры.

+1 за стремление дать Игре хорошую сплоченность (надлежащие обязанности).

Fuhrmanator 18.04.2012 16:50

Вместо того, чтобы сообщать игровому классу, что существует только один человек, почему бы не позволить ему получить этот ввод во время меню / инициализации игры? Если игроков больше, это можно решить с помощью некоторой формы ввода (выберите игроков в меню) до инициализации игрового класса.

Я думаю, что класс Game не должен беспокоиться о каких-либо реализациях классов Player, а также игнорировать пользовательский интерфейс.

Любой ввод пользователя должен обрабатываться классом HumanPlayer.

Интерфейс, который Игрок представляет для Игра, ортогонален поведению производных классов Игрок.

Тот факт, что реализация Принять поворот варьируется в зависимости от конкретного типа объекта Игрок, не должен вызывать беспокойства.

Я не уверен, что ты хочешь этого

public abstract class Player 
{
  int position;
  DecisionMaker decisionDependency;

  ...

  public void TakeTurn()
  {
    position += RollDice();
    GameOption option GetOptions(position);
    MakeDescion(option);
  }

  protected int RollDice()
  {
    //do something to get the movement
  }

  protected abstract void MakeDecision(GameOption option);

}

Public class ComputerPlayer : Player
{
  public ComputerPlayer()
  {
    decisionDependency = new AIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably delgate toan AI based dependency
  }
}

Public class HumanPlayer : Player
{
  public HumanPlayer()
  {
    decisionDependency = new UIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably interacting with the a UI or delgate to a dependency
  }
}

Я бы сказал, что класс Игра не должен заботиться о том, является ли он компьютерным игроком или человеком. Он всегда должен вызывать Принять поворот для следующего класса игрока. Если это игрок-человек, то ответственность за взаимодействие с пользователем и запрос у пользователя, что ему делать, возлагается на класс Игрок. Это означает, что он будет блокироваться, пока пользователь не примет решение. Поскольку обычно взаимодействие с пользовательским интерфейсом происходит в основном потоке приложения, важно только, чтобы блокирующий Принять поворот не блокировал приложение в целом, иначе пользовательский ввод не может быть обработан, пока Игра ожидает Принять поворот.

Если Game управляет игровым состоянием и, выполняя ввод-вывод, Game делает слишком много.

Вы хотите, чтобы игра была сосредоточена только на правилах, поворотах и ​​изменениях состояния. Игра не знает, кто такой игрок; он знает только, что у него есть игроки.

Вы хотите, чтобы игроки проверяли состояние игры и выполняли юридические действия во время своих ходов.

Люди-игроки и игра в целом обе используют общий пакет ввода-вывода, который показывает состояние игры и предлагает людям вводить данные.

Вы можете эффективно использовать Java Observable, сделав пакет ввода-вывода Observer игры. Таким образом, изменения состояния игры передаются в систему ввода-вывода для отображения или регистрации, или и того, и другого.

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

Это должно помочь решить проблему проигрывателя Пользователь и Компьютер.

У меня, вероятно, было бы не два класса HumanPlayer и ComputerPlayer, а один класс Player, который настраивается во время создания с правильной стратегией ввода.

Способ получения игроком информации для принятия решения о своем ходе в следующем ходу игры - это Только, который отличается (по крайней мере, от исходного описания проблемы), поэтому просто инкапсулируйте это в отдельной абстракции.

Какой бы высокоуровневый класс ни создавал игру, он должен также создать два набора игроков (один человек, другой - смоделированный компьютером) с правильной стратегией ввода для каждого, а затем просто передать эти объекты игроков игровому объекту. Тогда класс Game будет вызывать метод TakeTurn только для заданного списка игроков для каждого нового хода.

Другие вопросы по теме