首页 > 解决方案 > 如何改进我的代码设计以消除 Java 中对“instanceof”的需求?

问题描述

我决定尝试创建一个简单的游戏引擎,但我似乎被困在如何检查我的游戏对象的不同 Collider 类型之间的碰撞而不需要重复使用“instanceof”。例如,一些游戏对象可能使用 SphereCollider,而其他游戏对象可能使用 AABBCollider,两者都扩展了 Collider 类。

这是我的 GameObject 类中的代码。

public final boolean collidesWith(Collider c) {
    if (this.collider instanceof Collider3D) {
        if (c instanceof AABBCollider3D) {
            if (((Collider3D) this.collider).collidesWith((AABBCollider3D) c))
                return true;
        } else if (c instanceof SphereCollider3D) {
            if (((Collider3D) this.collider).collidesWith((SphereCollider3D) c))
                return true;
        } else if (c instanceof AABBCollider2D) {
            if (((Collider3D) this.collider).intersects((AABBCollider2D) c))
                return true;
        }
    } else if (this.collider instanceof Collider2D) {
        // Do 2D collision stuff...
    }
}

Collider3D 类

public abstract class Collider3D extends Collider {

public Collider3D(RigidBody rigidBody) {
    super(rigidBody);
}


public abstract boolean collidesWith(AABBCollider3D collider);

public abstract boolean collidesWith(SphereCollider3D collider);

public abstract boolean intersects(AABBCollider2D collider);

public abstract boolean intersects(CircleCollider2D collider);

}

AABBCollider3D 类

public class AABBCollider3D extends Collider3D {

private final Cuboid collisionBounds;


public AABBCollider3D(RigidBody rigidBody) {
    super(rigidBody);

    this.collisionBounds = new Cuboid();
}


@Override
public void update() {
    final Vector3f objectLocation = getRigidBody().getObject().getLocation().getPosition();
    final Matrix4f transformMatrix = getRigidBody().getObject().getTransformationMatrix();

    final float width = transformMatrix.m03();
    final float height = transformMatrix.m13();
    final float depth = transformMatrix.m23();

    // Set the collision bounds' location
    collisionBounds.getLocation().setPosition(
            objectLocation.x,
            objectLocation.y,
            objectLocation.z);  

    // Set the dimension of the collision bounds
    collisionBounds.setWidth(width);
    collisionBounds.setHeight(height);
    collisionBounds.setDepth(depth);
}

@Override
public boolean collidesWith(AABBCollider3D collider) {
    // Check collisions...
}

@Override
public boolean collidesWith(SphereCollider3D collider) {
    // Check collisions...
}

@Override
public boolean intersects(AABBCollider2D collider) {
    // Check collisions...
}

@Override
public boolean intersects(CircleCollider2D collider) {
    // Check collisions...
}

}

我想知道是否有更好的方法来做到这一点,主要有两个原因:

  1. 我听说使用“instanceof”关键字以这种方式检查对象类型是不良代码设计的一种解决方法
  2. 将来添加自定义 Collider 类型将需要我在此处以及在我的 Collider 类中扩展逻辑

标签: javaoopreflection

解决方案


Collider为每个特殊子类型在您的类型中添加一个方法。这是一种退化的访问者模式。

所以它看起来像:

interface Collider {
    boolean collidesWith(Collider other);

    // I've used the same name here,
    //   not necessary but makes things easier.
    boolean collidedBy(AABBCollider3D collider);
    boolean collidedBy(SphereCollider3D collider);
    // ...
}

每个子类型将如下所示:

public class AABBCollider3D implements Collider {
    public boolean collidesWith(Collider other) {
        return other.collidedBy(this);
    }

    public boolean collidedBy(AABBCollider3D collider) {
        // ... code for AABBCollider3D collided by AABBCollider3D.
    }
    public boolean collidedBy(SphereCollider3D collider) {
        // ... code for AABBCollider3D collided by SphereCollider3D.
    }
    // ...
}

这将在子类型之间创建相互依赖关系Collider。为了减少这种分裂的影响,在需要的地方和不需要的地方之间划分类型。


推荐阅读