【问题标题】:Abstract Base Class or Class?抽象基类还是类?
【发布时间】:2010-05-13 23:45:29
【问题描述】:

对于我的学期项目,我和我的团队应该制作一个包含游戏开发框架并演示 OOP 概念的 .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。创建一个 ALL 继承自的超级 ABC(抽象基类)

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

我应该使用哪个?还是有更好的办法?

【问题讨论】:

    标签: java class abstract-class


    【解决方案1】:

    嗯,如果不深入了解自己在做什么,就很难确定,即便如此,这也是相当主观的。但是,有一些事情需要考虑,这些事情可以告诉你。

    1. 他们是要实际实例化一个 Enemy,还是所有的敌人都真的需要是派生类型?如果您实际上不打算实例化 Enemies 而不是派生类型,那么它可能应该是接口或抽象类。

    2. 如果您希望在基类中提供实际行为,那么显然它需要是类而不是接口。

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

    4. 在基类中实现它们的方法应该在基类中实现。如果它们被覆盖没有意义,那么将它们设为最终的。

    5. 让类共享一个公共基类只有在它们真正共享行为或者您需要能够在代码中的某处以相同方式对待它们时才真正有意义。如果它们不是真的那么相似,那么它们可能不应该共享一个基类。例如,如果 Enemy 和 Player 都应该是可显示的,那么有一个通用的基类来处理它们的显示功能可能是有意义的。但如果 Enemy 是可显示的东西,而 Player 是一个更抽象的概念 - 就像游戏的控制器 - 并且不可显示,那么共享基类可能没有意义。一般来说,在构建类时,最好选择组合而不是继承,所以如果所讨论的类不会真正共享行为并且与公共基类没有真正的“is-a”关系,那么它们不应该共享一个公共基类。

    6. 希望您的基类只共享方法,而不是数据。换句话说,在继承树中,最好只有叶子是可实例化的。当您拥有包含实际数据的基类时,诸如equals() 之类的各种事物都会崩溃。这并不是说你不能这样做——人们总是这样做——但它可能会导致问题,如果不需要,最好避免。

    7. 更喜欢覆盖抽象方法。否则,在派生类中,您可能不会调用基类的方法或完全改变方法的作用。

    我确信我可以想出更多,但如果不真正熟悉您的项目,它肯定是相当通用的。在您提供的 3 个选项中,我可能会选择 2。3 似乎您可能会为不相关的类创建一个基类,而 1 会导致 Enemy 可实例化,您可能不需要并且肯定会做到这一点,以便您的继承层次结构中的叶子可以实例化。您最终可能仍会在具有 2 的基类中获得数据,但您更有可能只覆盖抽象方法,并且在派生类中更改行为时遇到的问题会更少。

    【讨论】:

      【解决方案2】:

      第四个选项是使用接口。

      interface Enemy {
      
          public void draw();
      
          . . .
      
      }
      

      如果您刚刚开始,我会避免您的第三种选择。让框架稍微发展一下,看看有没有必要。

      【讨论】:

        【解决方案3】:

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

        所以,如果是我做的:

        • 如果所有类都有共同点,请使用顶级abstract class 将这些功能/字段/数据收集到一个位置。
        • 如果不这样做,那么只有那些实际上有共同点的类才应该扩展较低级别的 abstract class

        如果只有方法是类的共同点,interfaces 也可以使用。但是,我总是发现迟早我会看到实现interface 的类具有相同的private 字段。此时,我将interface 转换为包含这些私有字段的abstract class(以节省代码行数)。

        【讨论】:

          【解决方案4】:

          只是书“更有效的c++”第271页的一个小答案:

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

          【讨论】:

          • 还有“Effective Java”,在这种情况下更适用。它说了一些类似的东西,但更具体的是 Java ——比如如果基类不都是抽象的,那么equals() 在混合派生类型和基类型时就不能正常工作。
          猜你喜欢
          • 2023-03-17
          • 1970-01-01
          • 2013-01-02
          • 2013-02-12
          • 2010-10-11
          • 2018-07-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多