我曾经在某个阶段被告知在大学(并随后在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
。 (双关语?)
照我最初的回答表明,使你们的目标给你的迭代类中的方法。 这不是在这种情况下理想的,因为该方法没有意义的父对象,并作为特德已经把那将是污染。
收缩你迭代做到这熟悉的目标方法的对象的对象的范围。 我觉得这是比较理想的方法,但可能不会在你的代码的目前的形式是可行的。
就个人而言,我避免instanceof
瘟疫一样,因为它让我觉得我完全错过了什么,但有时它是必要的。 如果你的代码是奠定了这种方式,你有没有办法缩小你迭代的对象的范围,那么instanceof
可能会工作得很好。 但是,这看起来像一个很好的机会,看到多态性如何使您的代码更易于阅读,并在未来保持。
我离开低于原来的答案,以保持意见的完整性。
/编辑
就个人而言,我不认为这是一个很好的理由使用instanceof
。 在我看来,你可以利用一些多态性,以实现自己的目标。
你有没有考虑做setUITweenManager(...)
的方法, GameObject
? 是否有意义做到这一点?
如果有一定道理,你可以有你的缺省实现什么也不做,和你的GameGroup
覆盖做你想要它做的方法。 在这一点上,你的代码可能只是看起来是这样,那么:
private void allocateUITweenManager() {
for(GameObject go:mGameObjects){
go.setUITweenManager(mUITweenManager);
}
}
这是多态性的行动,但我不知道这将是你目前的情况最好的办法。 它将使更多的意义遍历Collection
的UITweenable
如果可能的对象,而不是。
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:
你可以声明setUITweenManager
在GameObject
有一个什么也不做的实现。
你可以创建一个返回一个迭代器对所有的方法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