经常使用 instanceof 是好习惯吗?

Is it good practice to often use instanceof?

场景。我正在编写与游戏相关的代码。在那个游戏中,Player(它也是一个 class)有一个 Item 的列表。还有其他类型的项目继承自 Item,例如 ContainerItemDurableItemWeaponItem

显然,只有 List<Item> 对我来说非常方便。但是当我拿到玩家物品时,我唯一区分物品类型的方法是使用 instanceof 关键字。我确定我已经读过依赖它是不好的做法。

这种情况下可以用吗?还是我应该重新考虑我的所有结构?

您应该重新考虑您的结构,instanceof 在非元代码中几乎总是反模式的标志。使用abstract-关键字,如果合适,然后使用polymorphism来实现细节。

恕我直言,使用 instanceof 是一种代码味道。简而言之 - 它使您的代码过程化,而不是面向对象的。面向对象的方法是使用 visitor pattern

访问者模式还允许您在其上轻松构建 decoratorchain of responsibility,从而实现关注点分离,从而使代码更短、更清晰、更易于阅读和测试.

另外,您 真的 需要知道确切的 class 吗?不能利用多态性吗?毕竟 AxeWeapon 就像 Sword 一样。

您也许应该重新考虑并尝试使用多态性来实现您的 List<Item> 想法。

以下是一些可能对您的问题有所帮助的参考资料:


(来自 Is instanceof considered bad practice? If so, under what circumstances is instanceof still preferable? 的参考)

假设我正在编写一些库存代码:

public void showInventory(List<Item> items) {
    for (Item item : items) {
        if (item instanceof ContainerItem) {
            // container display logic here
        }
        else if (item instanceof WeaponItem) {
            // weapon display logic here
        }
        // etc etc
    }
}

这将编译并工作得很好。但它遗漏了面向对象设计的一个关键思想:你可以定义父 类 来做一般有用的事情,让子 类 填写具体的、重要的细节。

以上的替代方法:

abstract class Item {
    // insert methods that act exactly the same for all items here

    // now define one that subclasses must fill in themselves
    public abstract void show()
}
class ContainerItem extends Item {
    @Override public void show() {
        // container display logic here instead
    }
}
class WeaponItem extends Item {
    @Override public void show() {
        // weapon display logic here instead
    }
}

现在我们有一个地方可以查看,即 show() 方法,在我们所有子 类 中用于库存显示逻辑。我们如何访问它?简单!

public void showInventory(List<Item> items) {
    for (Item item : items) {
        item.show();
    }
}

我们将所有特定于项目的逻辑保留在特定项目子类 中。这使您的代码库更易于维护和扩展。它减少了第一个代码示例中长 for-each 循环的认知压力。它准备好 show() 可在您尚未设计的地方重复使用。

大家看得懂就可以了

将分支逻辑从它们自然属于的地方全部移动到 sub类 不一定是个好主意。它可能会产生错误的依赖;它可能会导致臃肿 类,并且可能难以导航和理解。

归根结底,都是关于如何在一维 space 中物理地组织具有多个关注点的代码。这不是一个小问题,它是主观的,并且没有灵丹妙药。

特别是,我们的行业继承了上个世纪基于那个时代的技术和经济限制而制定的许多经验法则。例如,工具非常有限;程序员非常昂贵;应用程序发展缓慢。

其中一些规则今天可能不再适用。

我不一定认为 instanceof 对于知道自己在做什么并使用它来避免不得不编写更复杂的代码来绕过它的编码器来说是不好的。凡事都有用,也有误用。

话虽如此,您提供的描述不需要instanceof。有多种方法可以在不使用 instanceof 的情况下实现这一点,并且(最重要的是)替代解决方案必须比使用 instanceof 更好。不要只是为了避免 instanceof 而使用非 instanceof 解决方案,因为你听说它很糟糕。

我认为对于您的场景,非实例化解决方案在使解决方案更易于扩展方面具有优势。

for (Item i: items) {
    if (ItemTypes.WEAPON_ITEM.equals(i.getType)) {
        processWeaponType(i);
    }

}

for (Item i: items) {
    if (WeaponItem.class.equals(i.getType)) {
        processWeaponType(i);
    }

}