Как создать два интерфейса для Java-класса: один только для чтения, один для чтения и записи? - PullRequest
6 голосов
/ 02 марта 2010

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

Игроки AI по очереди играют в карты на свое «поле».«стола» перед ними.Они могут атаковать картой со своего поля карту в поле другого игрока.Карты могут быть лицевой стороной вверх или лицевой стороной вниз.

Класс GameEngine позволяет игроку ИИ принять свой ход, вызвав метод GamePlayer.TakeTurn (GameEngine eng).Игрок может спросить игровой движок о поле защищающегося игрока, чтобы игрок мог принимать решения, основываясь на количестве карточек там и на том, какие из них открыты.Допустим, этот метод является GameEngine.GetDefendingField ()

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

У меня есть классы для Card, Field (ArrayList of Cards), GamePlayer и GameEngine.Есть ли какой-нибудь способ создать интерфейс для поля, чтобы GameEngine мог вернуть поле защищающегося игрока, но атакующий игрок не сможет его изменить?и без возможности атакующего игрока преобразовать поле защищающегося игрока во что-то, что можно изменить.

Могу ли я сделать так, чтобы GameEngine.GetDefendingField () возвращал доступный только для чтения интерфейс к полю, который нельзя преобразовать в поле?

тиа,

Ш-

Ответы [ 7 ]

11 голосов
/ 02 марта 2010

Если вы хотите достичь этого без копирования и без возможности приведения ссылки интерфейса «только для чтения» к соответствующему интерфейсу чтения-записи, вы можете попробовать использовать подход обертки.

Для интерфейса, подобного этому:

interface Info {
    String getInfoA();
    void setInfoA(String infoA);
    String getInfoB();
    void setInfoB(String infoB);
}

Вы можете создать оболочку только для чтения, которая игнорирует установщики или создает исключения, например:

class ReadonlyInfo implements Info {
    final Info instance;
    ReadonlyInfo(Info info) { 
         if (null == info) { throw new InvalidArgumentException(); } 
         instance = info; 
    }
    String getInfoA() { return instance.getInfoA(); }
    void setInfoA(String infoA) { /** silent ignore */ }
    String getInfoB() { return instance.getInfoB(); }
    void setInfoB(String infoA) { throw new IllegalStateException(); }
}

Используя подход обертки, вы можете использовать полиморфизм на стороне клиента, быть защищенным от преобразования ссылки, чтобы получить больше прав доступа и выбрать между беззвучным игнорированием вызываемых сеттеров или созданием исключения недопустимого доступа.

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

2 голосов
/ 02 марта 2010

Мне нравится идея использовать безопасность типов и полиморфизм для управления такого рода контролем доступа во время компиляции, особенно при обучении.

Хороший способ приблизиться к этому - иметь два полевых интерфейса. Может быть, Field и MutableField, где последний расширяет первый и добавляет методы изменения. Ваш FieldImpl может, конечно, реализовать оба.

Проблема, конечно, заключается в том, что вы хотите возвращать разные объявленные типы (Field или MutableField) из игрового движка в зависимости от того, чье поле вы используете. Если у вас есть одна модель, вы можете использовать что-то вроде getMyField () и getOtherPlayerField (), поскольку из описания следует, что при вызове getField () точно известно, какое именно поле им нужно.

2 голосов
/ 02 марта 2010

Вы говорите об основном Model-View-Controller здесь, с вашей Моделью, являющейся картой и полем, Controller - это GamePlayer и GameEngine, а представление пока еще не определено.Ваше Поле - это Ваша Модель, и вы захотите создать 2 разных интерфейса, которые будут иметь доступ к модели Поля, один только для чтения и один для чтения и записи.Ваша реализация для чтения-записи, скорее всего, просто вернет объект Field.Ваша реализация только для чтения разорвет элементы в поле и вернет только те, к которым они имеют доступ, сделав глубокую копию каждой возвращенной карты в поле.

1 голос
/ 02 марта 2010

Вы определенно можете сделать это. Этот подход называется 1001 * Затухание в литературе по объектным возможностям. Пока ограниченный тип не определен как подтип мощного типа, приведение объекта не даст больше полномочий.

1 голос
/ 02 марта 2010

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

0 голосов
/ 15 декабря 2017

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

Пример)

 public interface Info {
    void setA(String data);
    String getA();
 }

public interface ReadableInfo {
   String getA();
}

public class ReadableInformation implements ReadableInfo {
  private Info underlyingInfo;

  public ReadableInformation(Info info) {
    this.underlyingInfo = info;
  }

  @Override
  public String getA() {
    return underlyingInfo.getA();
  } 
}

С этим вы можете преобразовать изменяемый контракт (Info) в тот, который поддерживает только методы доступа (ReadableInfo). Но для этого необходимо передать объект другого типа (Info против ReadableInfo) в зависимости от того, кто является клиентом.

0 голосов
/ 02 марта 2010

Вы не можете определить интерфейс Java, который обеспечивает доступ только для чтения к полям / элементам, поскольку это детали реализации, определенные в классе. (Действительно, как вы храните данные внутри, это деталь реализации)

Я бы предложил создать две реализации вашего интерфейса Field: одну с доступом только для чтения, а другую с возможностью чтения / редактирования. Вы можете сделать так, чтобы каждый класс реализовывал интерфейс Field, возможно, с классом AbstractField в качестве родительского класса каждого для охвата общей функциональности.

Затем вы можете иметь конструкторы для каждого конкретного класса (только для чтения и чтения / записи) для преобразования между ними, если это необходимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...