【问题标题】:Casting and instance of or nearly empty interface?铸造和实例或几乎是空的接口?
【发布时间】:2012-05-22 15:36:02
【问题描述】:

我正在编写某种包含机器人、物品等的棋盘游戏。

在某一时刻,我需要获取机器人或可以具有能量的物品的能量值。其编程方式是每个机器人和每个具有能量的物品都有一个 EnergyContainer 类作为字段(以防止代码冗余)和一个能量值类。

现在,基本上,我调用一个接收元素的方法评估()。元素是机器人和物品扩展的抽象类。并非所有元素都有能量容器。如果可能的话,我需要在这个元素上调用 getEnergyContainer,但前提是它当然是一个拥有它的元素。

我可以想到几个解决方案:使用大量 instanceOf 然后进行强制转换。例如,如果元素是 instanceof 机器人,则将元素转换为机器人并在元素上调用 getEnergyContainer。这有一个明显的缺点,我需要对每个具有元素子类的能量执行此操作。

第二个解决方案是定义一个只包含 getEnergyContainer 方法的接口,并使所有具有类的能量都实现它。这个接口的唯一目的是方便一个方法,它几乎是空的。

除非有人有更好的想法,否则“最佳”解决方案是什么?我认为几乎空的接口用作标记接口,但这是唯一的目的,所以我有点反对它。

【问题讨论】:

  • 如果您至少发布代码的通用结构将会非常有用。
  • 对不起,由于种种原因,我不想这样做。对此造成的不便,我们深表歉意。

标签: java interface instanceof


【解决方案1】:

如果可能,我需要在这个元素上调用 getEnergyContainer,但前提是它当然是一个拥有它的元素。

您为什么不想在没有能量容器的元素上调用它?如果它没有有能量容器,要么返回对EnergyContainer 的某些“空对象”实现的引用,要么返回空引用。这取决于你以后想用它做什么——如果你可以轻松地实现某种“中性”能量容器,那么空对象模式是最简单的方法。否则,只需:

EnergyContainer container = element.getEnergyContainer();
if (container != null) {
    // Use the container
}

毫无疑问,有人会争辩说这在某种意义上是“不纯的” - 但几乎可以肯定它比大多数替代方案更简单

【讨论】:

  • 在“不纯”联盟(!)中,我会说它有点像:Animal fish = new Fish(); Leg leg = fish.getLeftLeg(); if (leg == null) etc.... 不确定我是否非常喜欢。
  • 感谢您的评论。它更简单,但我想我会同意 assylias 在这里我不太喜欢它作为解决方案。我个人更喜欢一些额外的代码,然后是稍微“不纯”的代码。对不起,但还是谢谢!
  • @assylias:我不想这样做太多,但我确实喜欢它的简单性。实际上,就其声音而言,能量容器是元素的可选部分。 IMO,诸如拥有单独界面之类的替代方案将增加复杂性,而不会使任何东西变得更清洁。你有什么更好的选择?
  • @JonSkeet 我理解你的意思 - 我可能会选择 EnergyElement 类/接口,Element 的子类/接口和 Robot 的超类/接口,使用所需的方法,但根据OP 这不是一个选项。因此,在这种情况下,您的解决方案可能是一个很好的折衷方案。
【解决方案2】:

最好的解决方案是将getEnergyContainer() 方法放在所有包含能量的元素的超类之一中,并在每个元素类中覆盖此方法。您可以将此方法设为abstract 以确保其被覆盖。你的超类可能是Element,因为你说:Element is an abstract class which robot and items extend.

【讨论】:

  • 感谢您的评论。这是不可能的,因为有多个超类,但没有一个包含所有具有元素的能量。它需要创建一个接口来做到这一点,所以我猜这是对它的投票。
  • @Sven:那么在这种情况下,您将不得不创建一个接口,原因有两个:1)它将更具可读性和可维护性,以及 2)开销略低于多次使用 instanceof如果能量元素相互延伸,也因为 instanceof 可能会出现问题。
【解决方案3】:

鉴于您的类层次结构使用带有接口的组合来提供默认的 EnergyContainer 行为

abstract class Element {

    EnergyContainer ec = new EmptyEnergyContainer();

    int getEnergyValue() {
        getEnergyContainer().getValue();
    }

    EnergyContainer getEnergyContainer() {
        return ec;
    }

    setEnergyContainer(EnergyContainer container) {
        this.ec = container;
    }
}

class Robot extends Element {

    public Robot() {
        this.ec = new ActiveEnergyContainer();
    }
}

class Item extends Element{

    public Item() {
        this.ec = new ActiveEnergyContainer();
    }
}

class Brick extends Element{
    // will have a EmptyEnergyContainer by default
}

EnergyContainer 的接口层次结构是这样的

interface EnergyContainer {
    int getValue();
    setValue(int value);
}

class EmptyEnergyContainer implements EnergyContainer {
    @Override
    int getValue() {
        return 0;
    }

    @Override
    setValue(int val) {
        throw Exception("Can not charge an empty container");
    }
}

class ActiveEnergyContainer implements EnergyContainer {
    int value;

    @Override
    int getValue() {
        return 17 + 3;  // calculate the value
    }

    @Override
    setValue(int val) {
        this.value = val // or do some funky calculation
    }
}

在运行时,您可以为您的对象设置新的 EnergyContainer 类型。如果您有多个父类,例如 Element,那么您必须遵循相同的模式,将默认行为添加到抽象父类并根据需要进行覆盖。

让默认行为为 getValue() 返回一个合理的默认值将帮助您不必到处使用instanceof

此代码的潜在改进是引入

  1. AbstractFactory 模式用于创建各种 EnergyContainer 变体
  2. 包含一个hasEnergy() 方法使您的代码更具可读性,而不是检查值 == 0
  3. 如果其他类似的父类包含类似的方法,则让Element 实现一个接口

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-03
    相关资源
    最近更新 更多