如何为Java类创建两个接口—一个只读接口,一个读写接口?

如何为Java类创建两个接口—一个只读接口,一个读写接口?,java,interface,Java,Interface,我正在用Java为一个两人纸牌游戏编写一个游戏引擎,我的学生将为此编写AI玩家 AI玩家将轮流在他们面前的“桌子”上的“场地”上打牌。他们可以用自己场上的一张牌攻击对方场上的一张牌。卡片可以面朝上或面朝下 GameEngine类允许AI玩家通过调用GamePlayer.TakeTurn(GameEngine eng)方法进行轮换。玩家可以向游戏引擎询问防守玩家的场地,这样玩家就可以根据那里的牌数以及哪些牌面朝上做出决定。假设这个方法是GameEngine.getDefensingField()

我正在用Java为一个两人纸牌游戏编写一个游戏引擎,我的学生将为此编写AI玩家

AI玩家将轮流在他们面前的“桌子”上的“场地”上打牌。他们可以用自己场上的一张牌攻击对方场上的一张牌。卡片可以面朝上或面朝下

GameEngine类允许AI玩家通过调用GamePlayer.TakeTurn(GameEngine eng)方法进行轮换。玩家可以向游戏引擎询问防守玩家的场地,这样玩家就可以根据那里的牌数以及哪些牌面朝上做出决定。假设这个方法是GameEngine.getDefensingField()

现在,我想确保进攻球员不能修改防守球员的场或防守球员场中的牌,并且进攻球员只能识别防守球员场中的正面牌

我有卡、场(一个卡的数组列表)、玩家和游戏引擎的类。有没有办法为球场创建一个界面,这样游戏引擎就可以返回防守球员的球场,而攻击球员无法更改球场?而且进攻球员不能将防守球员的场地重新塑造成可以改变的东西

我可以让GameEngine.GetDefensingField()返回一个只读接口到一个无法重铸到字段的字段吗

蒂亚


Sh-

不能定义强制对字段/成员进行只读访问的Java接口,因为这是类中定义的实现细节。(实际上,如何在内部存储数据是一个实现细节)

我建议创建两个字段接口实现,一个具有只读访问权限,另一个具有读/编辑权限。您可以让每个类实现Field接口,也许可以使用一个AbstractField类作为每个类的父类来覆盖公共功能


然后,如果需要,您可以为每个具体类(只读和读/写)设置构造函数,以便在这两个类之间进行转换。

您在这里谈论的是基本模型视图控制器,您的模型是卡片和字段,控制器是GamePlayer和GameEngine,视图尚未定义。字段就是您的模型,您需要创建两个不同的接口来访问字段模型,一个是只读的,一个是读写的。读写实现可能只返回Field对象。您的只读实现将遍历字段中的元素,只返回它们有权访问的元素,并在返回的字段中对每个卡进行深度复制。

我喜欢在编译时使用类型安全性和多态性来控制这种访问控制,尤其是在教学时

一个很好的方法是使用两个字段接口。可能是字段和可变字段,后者扩展了前者并添加了更改方法。当然,您的FieldImpl可以同时实现这两个功能


当然,问题是您希望根据所使用的字段从游戏引擎返回不同的声明类型(Field或MutableField)。如果只有一个模型,那么可以使用getMyField()和getOtherPlayerField()之类的函数,因为从描述中可以看出,调用getField()时,用户确切地知道他们需要哪个字段

您可以创建一个只有getter的只读接口。扩展该接口将是添加setter的读写器接口。

如果您希望在不复制的情况下实现这一点,并且不可能将只读接口引用强制转换为相应的读写接口,则可以尝试使用包装器方法

对于这样的界面:

interface Info {
    String getInfoA();
    void setInfoA(String infoA);
    String getInfoB();
    void setInfoB(String infoB);
}
您可以创建一个只读包装器,它会忽略setter或抛出异常,如:

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(); }
}
通过使用包装器方法,您可以在客户端使用多态性,避免强制转换引用以获得更多访问权限,并在静默忽略调用的setter或引发非法访问异常之间进行选择


注意:对于使用反射来检索包装对象的代码,您当然不安全。

您肯定可以这样做。这是一种在对象能力文献中被称为的方法。只要受限类型未定义为强大类型的子类型,强制转换对象将不会提供更多权限。

您可以创建两个契约-一个允许get和set,另一个仅支持get

(示例)


有了它,您可以将可变契约(
Info
)转换为只支持访问者的契约(
ReadableInfo
)。但这需要根据客户机的身份移交不同类型的对象(
Info
vs
ReadableInfo
)。

只是不要在接口名称前加上“I”,因为这不是Java编码标准。看@Steve:你说得对。多年的Eclipse插件编程带来的坏习惯。我从未注意到Eclipse在接口前面放了一个“I”。@Steve:《平台命名约定指南》规定:对于接口名称,我们遵循“I”-对于接口约定:所有接口名称都以“I”作为前缀。例如,“IWorkspace”或“IIndex”。此约定通过使接口名称更易于识别来帮助代码可读性。(微软COM接口订阅了这个约定)。@史蒂夫:我误解了你。Eclipse不会为您这样做。但是,Eclipse SDK的API(用于为Eclipse编写插件)和Eclipse本身都将此作为惯例。请注意,这并不妨碍AI播放器通过将对象强制转换到读写接口来访问字段。如果RW接口是RO接口的子类,那么就不能将RO对象强制转换为RW类型。在Java中,只能将对象强制转换为类型
 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();
  } 
}