【问题标题】:Issue with polymorphism and class hierarchy for objects in RTS gameRTS 游戏中对象的多态性和类层次结构问题
【发布时间】:2014-01-05 21:07:55
【问题描述】:

我正在用 Java 编写 RTS 游戏,但遇到了多态性问题。为了描述我的问题,我将列出游戏中的一些对象/类:(我将在括号中说明它们每个应该能够做什么)

  • 坦克(可以移动,可以攻击)
  • 塔(可以攻击)
  • 工厂(可以制造坦克)
  • 树(可以种植)
  • 老虎(可以移动,可以攻击,可以养殖)

在我看来,理想的方法是为每个独特的能力(即可移动、可攻击、可耕作、可构造)设置一个类。这是所有列出的对象都具有的不同能力。它们都是一个 GameObject(层次结构中的最高类,所有对象都有公共数据)。

Movable 包含用于移动对象的所有代码(计算速度、方向、新位置等)。 Attackable 包含用于跟踪目标、射击、更新射击等的代码。其他能力类也是如此。

所以在我看来这将是完美的:

public class Tank extends Movable, Attackable, GameObject {}
public class Tiger extends Movable, Attackable, Farmable, GameObject {}

显然 Java 不允许扩展多个类。而且我不知道如何使用接口来解决我的多态性和类层次结构问题。

有什么想法吗?目标当然是不重复游戏中多个对象共享的代码。

【问题讨论】:

  • 使用接口和/或组合。
  • @Radiodef 界面如何提供帮助?如果每个可以移动的对象都实现 Movable 我将不得不在几个类中编写/具有相同的代码。
  • 有一些方法可以为您节省一些代码。请记住,一个接口只能调用另一个作为实现的接口上的方法。我会写一个简单的答案来说明我的意思。

标签: java polymorphism hierarchy


【解决方案1】:

MovableAttackable是对象的几种可能行为,最好是接口

至于这些行为的实现——为了解决你重复代码的问题,你可以有单独的类——让我们将它们命名为服务——对于每个行为,例如。 移动服务攻击服务

然后,您可以将这些服务注入到您正在创建的对象中(例如。new Tank(myMoveService))或将对象传递给这些服务,例如。 MoveService.instance().move(myTank)

您可以通过调用委托方法为每个类运行一些自定义代码。

例子

interface Movable { public void onMove(); }

class Tank implements Movable {
    public void onMove() { 
        //tank moved! 
    }
}

class MoveService {
    public void move(Movable m) {
        // do what you need to do to move
        // invoke custom code by running a delegate method
        m.onMove();
    }
}

【讨论】:

  • 我喜欢这种方法,有服务。但是不需要实现接口,是吗? Tank 可以只拥有一个包含它需要的所有服务实例的列表,并通过 Tank 中的更新方法运行它们,还是我遗漏了什么?
  • 接口总是一个好主意,因为它们支持抽象。你可以去任何一种方式。它们肯定可以引用所需的服务,而不是传递对象。但是,如果你这样做,对象仍然应该实现 MovableAttackable 等接口,以便某个地方的攻击方法看起来像:void attack(可攻击 a)。所以如果一个Tank实现了Movable,它必须有一个move()方法(实现会使用MoveService我>)。如果你坚持接口,那么板子可以有一个方法 move(Movable m)
  • 完美,我会使用界面。谢谢。
【解决方案2】:

我建议将接口和组合结合起来。这是一个使用泛型保存一些代码的工厂示例。

interface UnitFactory<U extends Unit> {
    public U newUnit();
}

abstract class UnitFactoryBuilding<U, F extends AbstractUnitFactory<U>>
extends Building // assume 'Building extends GameObject'
implements UnitFactory<U> {

    final F factory;

    UnitFactoryBuilding(F factory) {
        this.factory = factory;
    }

    MapPosition getExitPoint() {
        /* return the point for the 'door' on the model */
    }

    @Override
    public U newUnit() {
        U unit = factory.newUnit();

        /* assume Building has a method that
         * returns the player that 'owns' it
         */
        getPlayer().deductResources(unit.getResourceCost());

        return unit;
    }
}

abstract class AbstractUnitFactory<U>
implements UnitFactory<U> {
    final Building owner;

    AbstractUnitFactory(Building owner) {
        this.owner = owner;
    }

    MapPosition getPositionForNewUnit() {
        return owner.getExitPoint();
    }
}

class TankFactory
extends AbstractUnitFactory<Tank> {

    TankFactory(TankFactoryBuilding owner) {
        super(owner);
    }

    @Override
    public Tank newUnit() {
        return new Tank(getPositionForNewUnit());
    }
}

class TankFactoryBuilding
extends UnitFactoryBuilding<Tank, TankFactory> {

    TankFactoryBuilding() {
        super(new TankFactory(this));
    }
}

这有点复杂,但类似的东西可以让您将实现分开,而不必复制大量代码。创建一种新的工厂只需要您编写最后两个类。

您不必为这种事情使用泛型,但它很方便,因为TankFactoryBuilding 可以作为TankFactory 访问其factory 成员。

【讨论】:

    【解决方案3】:

    Movable 和 Attackable 只是属性。

    考虑一个具有“隐身”能力的单位,当它处于隐身状态时不能受到攻击,但可以在不隐身时受到攻击。

    对象树是关于行为的。

    地形、单元、建筑等类更有意义, 因为他们会有大体相似的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多