我正在用Java编写一个游戏引擎用于双人纸牌游戏,我的学生将为其编写AI玩家。
AI玩家将轮流扑克牌放到他们面前“桌子”的一部分“球场”上。他们可以用自己场上的牌攻击另一个玩家场上的牌。卡片可能面朝上或面朝下。
GameEngine类通过调用GamePlayer.TakeTurn(GameEngine eng)方法允许AI玩家轮到他/她。玩家可以向游戏引擎询问防守玩家的场地,以便玩家可以根据那里的牌数以及哪些牌面朝上来做出决定。假设这个方法是GameEngine.GetDefendingField()
现在,我想确保攻击玩家无法修改防守玩家的场地或防御玩家场地中的牌,并且攻击玩家只能识别防守玩家场中的正面牌。
我有Card,Field(一个ArrayList of Cards),GamePlayer和GameEngine的课程。有没有办法为Field创建一个接口,以便GameEngine可以返回防守玩家的场地而攻击玩家无法改变它?并且没有攻击玩家能够将防守球员的场地改造成可以改变的东西。
GameEngine.GetDefendingField()是否可以将一个只读接口返回到一个无法重新转换为字段的字段?
TIA,
嘘 -
答案 0 :(得分:11)
如果要在不复制的情况下实现此目的,并且无法将只读接口引用转换为相应的读写接口,则可以尝试使用包装器方法。
对于这样的界面:
interface Info {
String getInfoA();
void setInfoA(String infoA);
String getInfoB();
void setInfoB(String infoB);
}
您可以创建一个只读包装器,它会忽略setter或throws异常,例如:
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或抛出非法访问异常之间进行选择。
注意:当然,对于使用反射来检索包装对象的代码,您将无法安全。
答案 1 :(得分:2)
你在这里谈论基本的模型 - 视图 - 控制器,你的模型是卡片和场,控制器是GamePlayer和GameEngine,而视图尚未定义。您的Field是您的模型,您可能希望创建两个不同的接口来访问Field模型,一个是只读的,一个是读写的。您的读写实现可能只返回Field对象。您的只读实现将通过字段中的元素进行翻录并仅返回他们有权访问的元素,并在返回的字段中对每张卡执行深层复制。
答案 2 :(得分:2)
我喜欢使用类型安全和多态来在编译时控制这种访问控制,尤其是在教学时。
接近它的一个好方法是拥有两个Field接口。也许Field和MutableField,后者扩展前者并添加更改方法。你的FieldImpl当然可以同时实现。
当然,问题是您希望根据您使用的字段从游戏引擎返回不同的声明类型(Field或MutableField)。如果你有一个模型,你可以使用类似getMyField()和getOtherPlayerField()的东西,因为从描述中可以看出,当调用getField()时,你确切地知道它们想要的字段。
答案 3 :(得分:1)
您可以创建只有getter的只读接口。扩展该接口将是添加setter的读写器接口。
答案 4 :(得分:1)
你绝对可以做到这一点。这是对象能力文献中称为attenuation的方法。只要限制类型未定义为强类型的子类型,则转换对象将不会提供更多权限。
答案 5 :(得分:0)
您无法定义强制对字段/成员进行只读访问的Java接口,因为这是在类中定义的实现细节。 (实际上,如何在内部存储数据是一个实现细节)
我建议您创建Field接口的两个实现,一个具有只读访问权限,另一个具有读取/编辑功能。您可以让每个类实现Field接口,也许使用AbstractField类作为每个类的父类来覆盖常用功能。
然后,如果需要,您可以为每个具体类(只读和读/写)创建构造函数,以便在两者之间进行转换。
答案 6 :(得分:0)
你可以创建两个合同 - 一个允许get和set,另一个只支持get。
实施例)
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
vs ReadableInfo
)。