抽象基类或类?

5

我的学期项目需要我和我的团队制作一个.jar文件(库文件,不可运行),其中包含游戏开发框架,并演示面向对象编程的概念。它应该是一个框架,另一个团队应该使用我们的框架,反之亦然。因此,我想知道我们应该如何开始。我们考虑了几种方法:
1. 从一个普通类开始

public class Enemy {
    public Enemy(int x, int y, int health, int attack, ...) {
        ...
    }
    ...
}
public class UserDefinedClass extends Enemy {
    ...
}

2. 从一个抽象类开始,用户定义的敌人必须继承抽象成员。

public abstract class Enemy {
    public Enemy(int x, int y, int health, int attack, ...) {
        ...
    }
    public abstract void draw();
    public abstract void destroy();
    ...
}
public class UserDefinedClass extends Enemy {
    ...
    public void draw() {
        ...
    }
    public void destroy() {
        ...
    }
}

3. 创建一个所有类都继承的超级ABC(抽象基类)

public abstract class VectorEntity {
    ...
}
public abstract class Enemy extends VectorEntity {
    ...
}
public class Player extends VectorEntity {
    ...
}
public class UserDefinedClass extends Enemy {
    ...
}

我应该使用哪个?还是有更好的方法吗?
4个回答

6

嗯,如果不深入了解你正在做什么,那就很难确定,即使深入了解也很主观。不过,有一些事情需要考虑,这可以告诉你。

  1. 他们是否要实例化一个Enemy,还是所有敌人都需要是派生类型?如果你不会实例化Enemies而是派生类型,那么它应该是接口或抽象类。

  2. 如果您想在基类中提供实际行为,则显然需要是一个类,而不是接口。

  3. 对于API需要存在但对您来说没有提供任何实现意义的方法应该是抽象的。

  4. 需要在基类中为其提供实现的方法应该在基类中具有实现。如果它们不被重写,那么将它们设置为final。

  5. 只有当它们真正共享行为或您需要能够在代码的某个地方将它们全部视为相同的对象时,使类共享一个公共基类才真正有意义。如果它们并不是非常相似,那么它们可能不应该共享一个基类。例如,如果Enemy和Player都应该可以显示,那么具有处理其显示功能的共同基类可能是有意义的。但是,如果Enemy是可以显示的东西,而Player是一个更抽象的概念-例如游戏的控制器-并且不能显示,则它们共享一个基类可能就没有意义。一般来说,在构建类时最好优先选择组合而不是继承,因此,如果相关的类不会真正共享行为,并且没有与公共基类具有“is-a”关系,则它们不应该共享公共基类。

  6. 最好使您的基类只共享方法而不是数据。换句话说,在继承树中,最好只有叶子节点可实例化。当您在基类中有实际数据时,会有各种问题,例如equals()。这并不是说您不能这样做-人们经常这样做-但如果不需要,最好避免使用。

  7. 最好重写抽象方法。否则,在派生类中,您可能无法调用基类的方法或完全更改该方法的功能。

我相信我可以想出更多的方案,但是如果不熟悉您的项目,那么这些方案可能会比较普遍。在您给出的3个选项中,我可能会选择第2个。第3个选项似乎是为不相关的类创建基类,而第1个选项将导致Enemy可被实例化,这可能是您不需要的,并且肯定会使得继承层次结构中的非叶子节点也能被实例化。使用第2个选项仍然可能会在基类中有数据,但您更有可能只覆盖抽象方法,并且在派生类中会有更少的行为改变问题。


1
第四个选项是使用接口。
interface Enemy {

    public void draw();

    . . .

}

如果你刚开始学习,我建议避免选择第三个选项。让框架先发展一段时间,看看是否需要它。


0

我的行为准则是,只要有多个类共享相同的操作/数据/方法/功能,它们应该是同一个抽象类的扩展。

所以,如果是我做的话:

  • 如果所有类都有共同点,请使用顶层的抽象类将这些功能/字段/数据集中在一个地方。
  • 如果没有,请仅使实际上具有共同点的那些类扩展较低级别的抽象类

如果方法是类所共有的唯一内容,则也可以使用接口。然而,我总是发现,迟早会看到实现接口的类具有相同的私有字段。此时,我将接口转换为保存这些私有字段的抽象类(如果仅仅是为了节省代码行数)。


0

这是来自《More effective c++》第271页的一个小答案:

“将不在层次结构末端的基类设为抽象类”。我懒得给你整个章节了,但作者列出了一些很好的理由。


还有一本名为“Effective Java”的书,它在这种情况下更加适用。它说的是类似的内容,但更具体地针对Java - 比如当混合派生类型和基础类型时,如果基类不都是抽象的,则equals()方法无法正常工作。 - Jonathan M Davis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接