避免的instanceof在Java中(avoid instanceof in Java)

2019-08-17 16:08发布

我曾经在某个阶段被告知在大学(并随后在upteen地方读取),使用instanceof只应作为“最后手段”。 考虑到这一点,有没有人能告诉是,如果下面的代码我是不得已而为之。 我有一个看看周围的堆栈溢出,但不能完全找到类似的情景 - 也许我错过了吗?

private void allocateUITweenManager() {
   for(GameObject go:mGameObjects){
      if (go instanceof GameGroup) ((GameGroup) go).setUITweenManager(mUITweenManager);
   }
}

哪里

  • mGameObjects是一个数组,仅其中一些是GameGroup类型
  • GameGroup是抽象类的子类GameObject
  • GameGroup使用接口UITweenable其具有方法setUITweenManager()
  • GameObject不使用接口UITweenable

我想我同样可以(并且可能应该)取代GameGroup在我上面的代码与UITweenable -我会问同样的问题。

是否有这样做,避免了另一种方式instanceof ? 此代码不能失败,因为这样的(我想,对吧?),但考虑到负面新闻instanceof似乎得到,我犯了OOP的一些大罪沿着有我行利用地方instanceof在这里?

提前致谢!

Answer 1:

我了解Visitor pattern在大学编译类,我想可能在您的情况适用。 考虑下面的代码:

public class GameObjectVisitor {

    public boolean visit(GameObject1 obj1) { return true; }
    .
    .
    // one method for each game object
    public boolean visit(GameGroup obj1) { return true; }
}

然后你可以把一个方法GameObject像这样的界面:

public interface GameObject {

    .
    .
    public boolean visit(GameObjectVisitor visitor);
}

然后将每个GameObject实现此方法:

public class GameGroup implements GameObject {

    .
    .
    .
    public boolean visit(GameObjectVisitor visitor) {
        visitor.visit(this);
    }
}

当你的复杂的继承层次这是特别有用的GameObject 。 对于你的情况你的方法是这样的:

private void allocateUITweenManager() {

    GameObjectVisitor gameGroupVisitor = new GameObjectVisitor() {
        public boolean visit(GameGroup obj1) {
            obj1.setUITweenManager(mUITweenManager);
        }
    };

    for(GameObject go:mGameObjects){
      go.visit(gameGroupVisitor);
   }
}


Answer 2:

编辑

有两个主要的东西,你可以在这里做,以减轻这一特定实例的自己instanceof 。 (双关语?)

  1. 照我最初的回答表明,使你们的目标给你的迭代类中的方法。 这不是在这种情况下理想的,因为该方法没有意义的父对象,并作为特德已经把那将是污染。

  2. 收缩你迭代做到这熟悉的目标方法的对象的对象的范围。 我觉得这是比较理想的方法,但可能不会在你的代码的目前的形式是可行的。

就个人而言,我避免instanceof瘟疫一样,因为它让我觉得我完全错过了什么,但有时它是必要的。 如果你的代码是奠定了这种方式,你有没有办法缩小你迭代的对象的范围,那么instanceof可能会工作得很好。 但是,这看起来像一个很好的机会,看到多态性如何使您的代码更易于阅读,并在未来保持。

我离开低于原来的答案,以保持意见的完整性。

/编辑

就个人而言,我不认为这是一个很好的理由使用instanceof 。 在我看来,你可以利用一些多态性,以实现自己的目标。

你有没有考虑做setUITweenManager(...)的方法, GameObject ? 是否有意义做到这一点?

如果有一定道理,你可以有你的缺省实现什么也不做,和你的GameGroup覆盖做你想要它做的方法。 在这一点上,你的代码可能只是看起来是这样,那么:

private void allocateUITweenManager() {
   for(GameObject go:mGameObjects){
       go.setUITweenManager(mUITweenManager);
   }
}

这是多态性的行动,但我不知道这将是你目前的情况最好的办法。 它将使更多的意义遍历CollectionUITweenable如果可能的对象,而不是。



Answer 3:

为什么原因instanceof不鼓励是因为在面向对象编程,我们不应该从外部检查对象的类型。 相反,惯用的办法是让自己反对使用行为重写的方法。 在你的情况下,一个可能的解决办法是定义boolean setUITweenManager(...)GameObject ,并让它返回true如果设置管理器中的特定对象是可能的。 但是,如果在很多地方出现这种格局,顶层类可以得到相当的污染。 因此有时instanceof是“两害取其轻”。

这种OPP方法的问题是,每个对象都必须“知道”所有可能的使用情况。 如果你需要一个新的功能,在你的类层次结构的工作,你必须将它添加到类本身,你不能有它的地方分开,就像在不同的模块。 这可以用一般的方法来解决访问者模式 ,为其他建议。 访问者模式描述研究对象的最一般的方法,当与多态性结合变得更加有用。

需要注意的是其他语言(特别是函数式语言)使用不同的原则。 与其让对象的“知道”他们的表现如何一切可能的行动,他们宣称对自己没有方法的数据类型。 相反,使用它们的代码检查他们如何利用人工模式匹配的代数数据类型 。 据我所知,最近的语言对Java有模式匹配的Scala 。 有一个关于斯卡拉如何实现模式匹配一个有趣的论文,其中比较了几种可能的方法: 匹配对象有图案。 布拉克埃米尔,马丁Odersky的,和约翰·威廉姆斯。

在面向对象编程的数据在类层次结构组织。 面向对象的模式匹配的问题是如何探索从外部此层次结构。 这通常涉及到分类可以通过运行时类型的对象,访问他们的成员,或确定一组对象的一些其他特征。 在本文中,我们比较六个不同的模式匹配技术:面向对象的分解,访客,类型检验/类型转换,典型案例,案例类和提取器。 该技术是在与简洁性,可维护性和性能九项标准进行比较。 本文介绍的情况下类和提取为两个新的模式匹配的方法,并表明他们的组合非常适用于所有的既定标准。

综上所述:在OOP中,你可以很容易地修改数据类型(如添加子类),但增加了新的功能(方法)需要进行更改很多类。 随着ADT可以很容易地增加新的功能,但修改的数据类型,需要修改的许多功能。



Answer 4:

用的instanceof的问题是,你可以从未来的对象层次结构的变化受到影响。 更好的方法是使用策略模式对你很可能会使用instanceof在案件。 制作用的instanceof你落入一个问题的策略是试图解决一个解决办法: 许多IFS。 有些人已经成立了一个社区。 反IF运动可能是一个笑话,但untipattern严重。 在一个长期的项目,保持10-20水平的if-else-如果可能是一个痛苦。 在你的情况,你最好把你的数组中的所有对象的公共接口和实施setUITweenManager通过接口对所有的人。

interface TweenManagerAware{
   setUITweenManager(UITweenManager manager);
}


Answer 5:

它始终是一个有点“腥”我同一收集混合不同类的对象。 有没有可能/有意义GameObjects的单个集合分割成多个集合,仅仅GameObjects之一,另一个UITweenables的? (例如使用由一类键控多重映射)。 然后,你可以去是这样的:

for (UITweenable uit : myMap.get(UITweenable.class)) {
  uit.setUITweenManager(mUITweenManager);
}

现在,你仍然需要一个instanceof当你插入到地图上,但它是更好的封装-从客户端代码谁并不需要知道这些细节隐藏

PS我不是一个狂热的关于所有SW“规则”,但谷歌“里氏替换原则”。



Answer 6:

你可以声明setUITweenManagerGameObject有一个什么也不做的实现。

你可以创建一个返回一个迭代器对所有的方法UITweenable在阵列情况下GameObject实例。

并且有有效地隐藏了一些抽象内调度其他方法; 例如访问者或适配器模式。


......我犯了OOP的一些大罪沿着有我行利用地方instanceof在这里?

不是真的(IMO)。

最差问题instanceof是当你开始使用它来测试实现类。 这是特别糟糕的原因是,它使得它难以添加额外的课程,等等。 这里instanceof UITweenable东西似乎并没有引入问题,因为UITweenable似乎是更基本的设计。


当你做出这些各种各样的判断,这是最好理解为什么(据说)不良结构或使用被自称是坏的原因。 那你看你具体使用情况和弥补这些理由是否适用,以及是否另外,您所看到的是在您的使用情况真是再好不过了。



Answer 7:

当你需要做的所有的游戏对象的东西,并保持一个单独的容器仅供GameGroup对象,你可以使用的容器mGameObjects。

这将使用更多的内存,并且当你添加/删除您必须更新两个容器对象,但它不应该是一个显着的开销,而且它非常有效地让你遍历所有的对象。



Answer 8:

这种方法的问题是,它通常不会出现在一个地方只在你的代码,从而使得它或多或少痛苦添加界面的另一个实现的未来。 无论以避免它取决于你的考虑。 有时YAGNI可以适用的,这是最直接的方式。

替代已经被其他人则建议,例如Visitor模式。



Answer 9:

我有一个办法另一项建议避免instanceof

除非当你创建一个你使用的是普通的工厂,此刻的GameObject ,你知道它是什么具体类型。 所以你可以做的是通过任何GameGroup是你创建一个可观察的对象,并让他们侦听器添加到它。 它的工作是这样的:

public class Game {
    private void makeAGameGroup() {
        mGameObjects.add(new GameGroup(mUITweenManagerInformer));
    }

    private void allocateUITweenManager() {
        mUITweenManagerInformer.fire(mUITweenManager);
    }

    private class OurUITweenManagerInformer extends UITweenManagerInformer {
        private ArrayList<UITweenManagerListener> listeners;

        public void addUITweenManagerListener(UITweenManagerListener l) {
            listeners.add(l);
        }

        public void fire(UITweenManager next) {
            for (UITweenManagerListener l : listeners)
                l.changed(next);
        }
    }
    private OurUITweenManagerInformer mUITweenManagerInformer = new OurUITweenManagerInformer();
}

public interface UITweenManagerInformer {
    public void addUITweenManagerListener(UITweenManagerListener l);
}

public interface UITweenManagerListener {
    public void changed(UITweenManager next);
}

是什么吸引我到这个解决方案是:

  • 由于UITweenManagerInformer是一个构造函数参数GameGoup ,你不能忘记将它传递一个,而用一个实例方法你可能会忘记调用它。

  • 它具有直观意义,我认为信息的对象需要(像的方式GameGroup需要当前的知识UITweenManager )应作为一个构造函数参数传递-我喜欢把这些作为前提条件对现有的对象。 如果没有当前的知识UITweenManager ,你不应该创建一个GameGroup ,而这个解决方案强制执行。

  • instanceof从未使用过。



文章来源: avoid instanceof in Java