在没有 instanceof 的情况下使用由超类集合的子类实现的接口

Using interface implemented by subclass from a superclass collection without instanceof

我正在编写一个 2D 小游戏,所有游戏元素都是 GameObject 的子class。 class 游戏有一个 GameObject 集合。我的问题是,当玩家执行一个动作时,我通过 GameObject 的集合来找到玩家面前的东西,然后我只想使用由 GameObject 的 subclasses 实现的接口方法而不使用instanceof 和铸造。

Here is a (very) simplified class-diagram of the situation

我试图实现访问者模式,但我希望函数 visit()ActivableObstacle 作为参数而不是 TVWall.

这是一个代码示例:

class Game {
    private ArrayList<GameObject> gameObjects;
    ...
    public void actionPerformed(...) {
        GameObject current;
        //find the focused Game object
        ...

        //What's easiest but I don't want
        if(gameObjects instanceOf Obstacle) {
            ((Obstacle) current).aMethod()
            ...
        } else if (gameObjects instanceOf Activable) {
            ((Activable) current).activate()
            ...
        }
        ...

        //What visitor allow me
        public void watchVisit(TV tv) {
            tv.watch();
        }
        public void hitVisit(Wall w) {
            //...
        }

        //What I want
        public void activableVisit(Activable a) {
            a.activate();
        }
        public void walkVisit(Obstacle o) {
            //...
        }
        ...
}

游戏对象:

class GameObject {
    public void acceptWatch(Game g) {
        //Do nothing, only implemented in class TV
    }
    //Same with wall
    ...
}

电视:

class TV extends Item implements Activable {
    public void acceptWatch(Game g) {
        //this works if watchVisit take a "TV" but not if it's a "Activable"
        g.watchVisit(this);
    }
    public void watch() {
        ...
    }
    ...
}

我该如何解决这个问题?

与其在 GameObject 中为 watchTV()hitWall() 创建所有这些单独的方法,不如在 GameObject Abstract 中使用任何公共变量( nameactivatableobstacle 等)与一个名为 doButtonOneActivity().

Abstract 方法

然后制作您的其他对象,如 TVWall,扩展 GameObject 并覆盖 doButtonOneActivity() 方法,无论单击时该特定项目会做什么。

现在您的 Game class 只需在 GameObject 上调用 doButtonOneActivity() 对象本身就会知道它需要做什么,而无需您手动做到这一点。

希望对您有所帮助!

游戏:

public class Game implements Serializable, IClusterable {
    private static final long serialVersionUID = 1L;

    private ArrayList<GameObject> gameObjects;

    public void actionPerformed(GameObject current) {

        // Let the object do whatever it's supposed to do on button press, in either case.
        current.doButtonOneActivity();

        if(current.isActivatable()){
            // Do whatever extra thing you need to do if this one is Activatable...
            System.out.println("Hey, this thing is activatable!");
        } else if (current.isObstacle()){
            // Do something an obstacle needs you to do
            System.out.println("Hey, this thing is an obstacle!");
        }

    }
}

抽象的 GameObject。

public abstract class GameObject implements Serializable, IClusterable {
    private static final long serialVersionUID = 1L;

    public String name;
    private boolean activatable;
    private boolean obstacle;

    public GameObject(String name, boolean activatable, boolean obstacle) {
        super();
        this.name = name;
        this.activatable = activatable;
        this.obstacle = obstacle;
    }

    public abstract void doButtonOneActivity();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isActivatable() {
        return activatable;
    }

    public void setActivatable(boolean activatable) {
        this.activatable = activatable;
    }

    public boolean isObstacle() {
        return obstacle;
    }

    public void setObstacle(boolean obstacle) {
        this.obstacle = obstacle;
    }
}

电视,它扩展了GameObject并填充了它需要的方法。

public class TV extends GameObject implements Serializable, IClusterable {
    private static final long serialVersionUID = 1L;

    public TV(String name, boolean activatable, boolean obstacle) {
        super(name, activatable, obstacle);
    }

    @Override
    public void doButtonOneActivity() {
        if(isActivatable()){
            // do whatever I need to do as a TV when I am activated...
           }
           if (isObstacle()){
            // do whatever I need to do as a TV when I am activated as an obstacle...
           }
           System.out.println("I'm a TV and I was called. My name is: " + getName()); 

    }
}

Wall,它扩展了GameObject并填充了它需要的方法。

public class Wall extends GameObject implements Serializable, IClusterable {
    private static final long serialVersionUID = 1L;

    public Wall(String name, boolean activatable, boolean obstacle) {
        super(name, activatable, obstacle);
    }

    @Override
    public void doButtonOneActivity() {    
        if(isActivatable()){
         // do whatever I need to do as a Wall when I am activated...
        }
        if (isObstacle()){
         // do whatever I need to do as a Wall when I am activated as an obstacle...
        }
        System.out.println("I'm a wall and I was called. My name is: " + getName()); 

    }
}

我认为很难找到一个问题的唯一答案。它涉及到设计和意图,并且总是需要权衡 space,很少有明确的答案。

不过我觉得你应该看看Composite Pattern。复合模式本质上采用一组不同的对象,并以相同的方式对待它们。它通过在所有更高级别的对象 Component 上实现所有接口来实现这一点,以便所有对象都继承一组通用的方法和属性。

在您的示例中,您可以将 GameObject 定义为您的组件,并实现 ObstacleActivatable 接口。还要添加一些辅助方法,例如 isObstacleisActivatable,这样您就可以在不强制转换的情况下进行测试。现在您可以让访问者遍历列表并且只对某些对象执行操作,这就是我认为您所追求的。