了解何时使用继承以及如何使用



我目前不确定继承如何最适合我的Java程序,或者是否最好使用接口来实现。下面介绍了我正在谈论的上下文的一个例子。

想象一下,我正在制作一个游戏,我的游戏中有敌人。这些敌人都有各种共同的变量和行为,但在某种程度上都是独一无二的。例如,敌人1,

class Enemy1 {
float x, y, width, height;
public Component(float x, float y, float width, float height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void AI() {
//some code
}
}

和敌人2,

class Enemy2 {
float x, y, width, height;
public Component(float x, float y, float width, float height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void AI() {
//some different code
}
}

因此,对于这两个独立的敌人,我理解(或至少相信(我可以将Enemy2更改为类似于

class Enemy2 extends Enemy1 {
@Override
public void AI() {
//some different code
}
}

但是,现在如果我有五个不同类型的 AI 代码,每个敌人可能拥有五种不同类型的 AI 代码,然后不是为每个敌人定义类中的 AI 行为代码,而是我想拥有另一个包含所有 AI 的类,允许我扩展/实现 AI 类/接口并选择我想要的那个。

我不确定这样做的最佳方法是什么。简而言之,我想有不同的敌人共享属性,每个敌人都有相似但不同的功能(例如AI代码(,但总的来说仍然非常独特。只用类来做这件事会更有效吗?接口?我将如何去做呢?我会让一个班级画出一个班级吗?或者只是一个类扩展一个类并同时实现一个接口?我不太确定使用哪种最佳继承结构。

对不起,如果我犯了任何根本性的误解或错误。

任何帮助将不胜感激。

你可以用一个过程函数做一个抽象的AI类:

abstract class AI
{
public abstract void process(Enemy en);
}

然后你可以扩展它。

class AISipleMovement extends AI
{
@Override
public void process(Enemy en)
{
en.moveRandomly(); //Insert actual code here
}
}

或只是:

class AIStayFrozen extends AI
{
@Override
public void process(Enemy en)
{
}
}

在敌人类中:

class Enemy1 {
float x, y, width, height;
AI ai;
public Enemy1(float x, float y, float width, float height, AI ai) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.setAI(ai)
}
public void setAI(AI aiIn)
{
this.ai = aiIn;
}
public void AI() {
ai.process(this);
}
}

当然,这是最小的例子,你应该覆盖ai不是空,以防止NullPointerException和类似的东西。

类 Enemy2 扩展 Enemy1

不过,这在语义上有意义吗?Enemy2也是Enemy1吗? 对游戏一无所知,假设Enemy1是一只可怕的狼,Enemy2是一只熊。 熊不是可怕的狼,所以这种结构是没有意义的。 但如果Enemy2是可怕的狼群领袖,那么这是有道理的,因为它也是一个可怕的狼。

我想有另一个包含所有AI的类

这对我来说听起来很奇怪。 一个包含其他对象所有逻辑的对象,他们从中提取逻辑? 这听起来非常接近"上帝对象"的反模式。 您可能应该将逻辑保留在拥有它的对象上。


根据描述,听起来你想要一个抽象类。 一个基Enemy类,它本身不能实例化,但它拥有所有敌人的共性。 像这样:

abstract class Enemy {
float x, y, width, height;
public Enemy(float x, float y, float width, float height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
abstract public void AI();
}

那么任何敌人都会从中继承:

class DireWolf extends Enemy {
public DireWolf(float x, float y, float width, float height) {
super(x, y, width, height);
}
public void AI () {
//...
}
}

实际上,编译器需要任何扩展Enemy的类来实现AI()。 因此,公共功能和公共接口保存在抽象基类中,从它继承的每个类都将向该接口提供其实现。

编辑

此方法需要每个子类都有一个构造函数。 这可能对您有利,例如,如果每个 DireWolf 的大小相同,则构造函数可能如下所示:

public DireWolf(float x, float y) {
super(x, y, 64, 64);
}

然后你可以按如下方式初始化你的狼:

Enemy wolf = new DireWolf(0, 0);

最新更新