Java 组合具有不同字段的子类以提高效率?

Java 组合具有不同字段的子类以提高效率?,java,android,oop,Java,Android,Oop,我正在开发一款Java Android游戏。这里的游戏通常需要使用内存池来避免垃圾收集 为了便于论证,假设我有以下几类: // Enemy in the game class Enemy { int x; int y; abstract void update(); } // Enemy that just bounces around class Roamer extends Enemy { int direction; ... } // Enemy that chases a player

我正在开发一款Java Android游戏。这里的游戏通常需要使用内存池来避免垃圾收集

为了便于论证,假设我有以下几类:

// Enemy in the game
class Enemy { int x; int y; abstract void update(); }
// Enemy that just bounces around
class Roamer extends Enemy { int direction; ... }
// Enemy that chases a player
class Chaser extends Enemy { Player target; ... }
// maybe 20 more enemy types...
当您需要使用内存池时,使用子类是一件痛苦的事情,例如,说出您需要多少漫游者和追逐者对象,而不仅仅是您可能需要多少敌人对象。此外,要更新的虚拟函数调用可能会很慢

我想到的另一种选择是将所有这些类合并成一个类

class Enemy
{
    int direction;
    Player target;
    int type; // i.e. ROAMER_TYPE, CHASER_TYPE
}
然后,我将有一个update函数来检查“type”变量并相应地进行更新。这显然是一种更像C的方法。不过,这让人感觉很不舒服,因为例如,漫游者敌人会有一个从未使用过的“目标”变量。我不太可能一次有超过100个敌人在我的记忆中,所以这真的不是什么大问题。我只是想在好的代码和速度之间取得一些折衷

有人对如何最好地组织这一点有什么意见吗?像这样合并课程是不是太过分了?这是个好主意吗?我应该只使用常规的类树吗


我知道这个问题没有正确的答案,但我想要一些不同的观点。

您是否确实验证了没有内存池会出现性能问题

假设您确实需要使用内存池,为什么不为每种类型的对象设置一个内存池,并让它们“增长”到您需要的对象数量?如果对象类型之间的平衡随着时间的推移而急剧变化,那么效率就会降低,但是您描述的方法也会降低效率(未使用的字段),并且维护起来似乎不太有趣

此外,要更新的虚拟函数调用可能会很慢


胡说!进行虚拟函数调用的成本是JIT编译的应用程序中的两条指令。即使在解释模式下,我也只希望它需要十几条本机指令。这是在噪音中。。。除非你每秒打数千万次这样的电话


我支持@Laurence回答的要点。只有在您确认(即通过测量)确实存在需要解决的性能问题后,才能花时间进行性能测试。如果忽略这一点,您可能会使代码变得比需要的更复杂、更难维护。而且,如果你天真地试图让它变得更好,那么你很可能会让性能变得更差。。。尤其是随着Android JIT编译器越来越好。

在这里,组合比继承更有效吗?例如,类似这样的内容:

class Enemy {
    int direction;
    Player target;
    Strategy updater;
}
interface Strategy {
    void update(Enemy state);
}
class Roamer implements Strategy {
    public void update(Enemy state) {
        //Roamer logic.
    }
}
class Chaser implements Strategy {
    public void update(Enemy state) {
        //Chaser logic.
    }
}
那么您只需要每个特定策略的一个实例。例如,当前充当漫游者的池中的所有敌人对象都将使用单个漫游者实例进行更新。这样可以避免将所有逻辑保留在一个类中,这可能会变得过于复杂

如果这不是一款Android游戏,那么你可以在敌方类中放置一张地图,以保持一种策略需要的敌方状态,而另一种策略不需要。由于我们必须避免分配,这可能会触发垃圾收集器,但是,最好像您所说的那样,将所有需要的成员都放在敌方类中


您可以在敌方类中使用预先分配的数组作为状态。例如,追逐者将始终在状态数组的特定索引中保留对其追逐的玩家的引用。但是,这变得非常复杂。

您可以从这里复制框架内部的内存池(请参阅pool、Poolable、PoolableManager、Pools、FinitePool、SynchronizedPool):

如果这样做,请确保将它们放在您自己的包命名空间中


没有必要为了对象池而丢失对象定向。如果那里的代码仍然非常通用,让您感到害怕,请使用相同的方法为您的对象创建一个简化的通用池。

>您是否确实验证了没有内存池会出现性能问题?如果您经常取消分配工具,Android GC可以在随机时间运行大约300毫秒,因此,如果您想要一致的速度,例如30 fps,那么在大多数游戏中使用内存池是不需要动脑筋的。>如果对象类型之间的平衡随着时间的推移而急剧变化,那么效率就会降低,但是您描述的方法也会降低效率。嗯,我需要将这一点与虚拟函数调用的速度相平衡。Android最近才在2.0版中进行了JIT编译。即使在该版本中,它也不能为普通用户启用。这只是开发人员测试的一个选项。不管怎样,许多手机永远也不会升级到这么远。这就是说,在谷歌I/O实时游戏对话中有一个关于这一点的图表,虚拟函数调用并没有那么糟糕。JNI和通过接口调用更糟糕:“进行虚拟函数调用的成本是JIT编译的应用程序中的两条指令。”还有可能出现缓存未命中,这比简单的两条额外指令对性能的影响要糟糕得多。如果使用第二种方法,请确保使用枚举而不是int,为确保类型安全,android下严格不建议使用.enum。我真的不喜欢这种方法,因为这意味着我必须键入两次文件名(一次用引号,一次用常量)。