[英]Avoding instanceof in Java
在我的应用程序中,我有一个2d实体数组来表示网格。 网格中的每个位置都可以为空或由实体占用(在这种情况下,它只是一个人或墙)。 现在我使用instanceof
来检查一个实体是一个人还是一个墙。
我在考虑为每个实体提供一个方法,该方法返回一个enum
其类型的enum
,即一个wall实体将返回EntityType.WALL
。 我想知道,这是去除使用的最好的想法instanceof
或者是instanceof
适用于这种情况?
使用Tell,Do not Ask :而不是询问对象是什么,然后对此作出反应,告诉对象该做什么,然后墙或人们确定他们如何做他们需要做的事情。
例如:
而不是像这样的东西:
public class Wall {
// ...
}
public class Person {
// ...
}
// later
public class moveTo(Position pos) {
Object whatIsThere = pos.whatIsThere();
if (whatIsThere instanceof Wall) {
System.err.println("You cannot move into a wall");
}
else if (whatIsThere instanceof Person) {
System.err.println("You bump into " + person.getName());
}
// many more else branches...
}
做这样的事情:
public interface DungeonFeature {
void moveInto();
}
public class Wall implements DungeonFeature {
@Override
public void moveInto() {
System.err.println("You bump into a wall");
}
// ...
}
public class Person implements DungeonFeature {
private String name;
@Override
public void moveInto() {
System.err.println("You bump into " + name);
}
// ...
}
// and later
public void moveTo(Position pos) {
DungeonFeature df = currentPosition();
df.moveTo(pos);
}
这有一些优点。
首先,每次添加新的地下城功能时,您都不需要调整巨型树。
其次,地牢特征中的代码是自包含的,逻辑全部在所述对象中。 您可以轻松测试并移动它。
以精确方式移除instanceof
的理论解决方案是访问者模式的使用。 工作原理是需要知道另一个元素是墙或人的对象将自身作为参数调用该对象,并且该特定对象回调,从而提供有关其类型的信息。
例,
public class Person {
void magic() {
if(grid.getAdjacent() instanceof Person) {
Person otherPerson = (Person)grid.getAdjacent();
doSomethingWith(otherPerson);
} else if(grid.getAdjacent() instanceof Wall) {
Wall wall = (Wall)grid.getAdjacent();
doOtherThingWith(wall);
}
}
}
可以变成
public class Person extends Entity {
void magic() {
grid.getAdjacent().visit(this);
}
void onVisit(Wall wall) {
doOtherThingWith(wall);
}
void onVisit(Person person) {
doSomethingWith(person);
}
public void visit(Person person) {
person.onVisit(this);
}
}
public class Wall extends Entity {
public void visit(Person person) {
person.onVisit(this);
}
}
如果您按照其他答案并实现访问者模式或使用枚举,您将不会犯错。
但是,它也可能有助于思考您想要对切换逻辑(无论是实例还是访问者)做什么,因为有时候有一种更简单的方法可以做到这一点。
例如,如果您要做的只是检查实体是否以阻塞方式占用网格,那么您只需通过接口向每个实体添加一个方法boolean isSolid()
。 你可以使用默认方法来获得额外的美感:
public interface GridPhysics {
default boolean isSolid() {
return true;
}
// other grid physics stuff
}
public class Wall implements GridPhysics {
// nothing to do here, it uses the default
}
// in your game logic
public boolean canMoveTo(GridPhysics gridCell) {
return !gridCell.isSolid() && otherChecks();
}
您可能还想查看实体组件系统(例如Artemis ),它基本上将“构图优于继承”的概念发挥到极致。
我会让person和wall继承自一个抽象的超类(例如Tile),它有一个方法getType()返回一个enum或int并在Wall和Person中实现这个方法返回相应的
答案是非常好的,这里没什么可说的,但如果我在这种情况下,如果它被允许比我本来已经去了一个2d int数组可能值0(默认分配为空)和1,2为人或墙。
正如在另一个问题中所提到的,现代Java编译器在诸如instanceof之类的操作中非常有效。 你可以使用它。
事实上,其中一个提供了测试instanceOf和字符串比较的答案,而instanceOf明显更快。 我建议你坚持使用它。
ligi的回答是肯定的。 (无论是谁投票,我都不知道他们在想什么。)作为替代方案,请考虑以下事项:
abstract class Tile
{
public final EntityType type;
protected Tile( EntityType type )
{
this.type = type;
}
}
abstract class Pedestrian extends Tile
{
public Pedestrian()
{
super( EntityType.PEDESTRIAN );
}
}
abstract class Wall extends Tile
{
public Wall()
{
super( EntityType.WALL );
}
}
这背后的基本原理是实体的“类型”是实体的永久特征,因此适合在构造函数中指定并在final
成员字段中实现。 如果它是由虚方法(java用语中的非最终方法)返回的,那么后代可以在一个时间点自由返回一个值,而在另一个时间点返回另一个值,这会造成严重破坏。
哦,如果你真的无法忍受公众最后的成员,请继续为它添加一个吸气剂,但我的建议是,没关系纯粹主义者,没有吸气剂的公共最终成员完全没问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.