简体   繁体   English

抽象基类还是类?

[英]Abstract Base Class or Class?

For my semester project, my team and I are supposed to make a .jar file (library, not runnable) that contains a game development framework and demonstrate the concepts of OOP. 对于我的学期项目,我的团队和我应该创建一个包含游戏开发框架并展示OOP概念的.jar文件(库,不可运行)。 Its supposed to be a FRAMEWORK and another team is supposed to use our framework and vice-versa. 它应该是一个框架,另一个团队应该使用我们的框架,反之亦然。 So I want to know how we should start. 所以我想知道我们应该如何开始。 We thought of several approaches: 我们想到了几种方法:
1. Start with a plain class 1.从普通课开始

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

2. Start with an abstract class that user-defined enemies have to inherit abstract members 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. Create a super ABC (Abstract Base Class) that ALL inherit from 3.创建ALL继承的超级ABC(抽象基类)

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

Which should I use? 我应该使用哪个? Or is there a better way? 或者,还有更好的方法?

Well, it's a bit hard to say for sure without knowing in depth what you're doing, and even then it's rather subjective. 嗯,如果不深入了解你正在做什么,有点难以肯定,即便如此,这也是相当主观的。 However, there are some things to consider which could tell you. 但是,有些事情需要考虑,可以告诉你。

  1. Are they going to actually instantiate an Enemy, or do all enemies really need to be of a derived type? 他们是否会真正实例化敌人,或者所有敌人真的需要属于派生类型吗? If you're not actually going to be instantiating Enemies rather than derived types, then it should likely be either an interface or an abstract class. 如果你实际上并不是要实例化Enemies而不是派生类型,那么它应该是一个接口或一个抽象类。

  2. If you're looking to provide actual behavior in your base class, then obviously it needs to be a class rather than an interface. 如果您希望在基类中提供实际行为,那么显然它需要是一个类而不是一个接口。

  3. Methods which need to be there for the API but don't make any sense for you to be providing any implementations for should be abstract. 需要为API提供但对您提供任何实现没有任何意义的方法应该是抽象的。

  4. Methods where it makes good sense to have implementations for them in the base class should have implementations in the base class. 在基类中实现它们很有意义的方法应该在基类中有实现。 And if it doesn't make good sense for them to be overridden, then make them final. 如果它们被覆盖的意义不大,那么让它们成为最终的。

  5. Making classes share a common base class really only makes sense if they're really sharing behavior or you need to be able to treat them all the same somewhere in your code. 让类共享一个共同的基类真的只有在他们真正分享行为或者你需要能够在代码中的某个地方处理它们时才有意义。 If they're not really all that similar, then they probably shouldn't share a base class. 如果他们真的不那么相似,那么他们可能不应该共享一个基类。 For instance, if both Enemy and Player are supposed to be displayable, it may make sense to have a common base class which handles their display functionality. 例如,如果Enemy和Player都应该是可显示的,那么拥有一个处理其显示功能的公共基类可能是有意义的。 But if Enemy were something that was displayable, and Player was a more abstract concept - like the controller of the game - and wasn't displayable, then it probably wouldn't make sense for them to share a base class. 但是如果Enemy是可以显示的东西,并且Player是一个更抽象的概念 - 比如游戏的控制器 - 并且不可显示,那么它们分享基类可能没有意义。 In general, it's better to prefer composition rather than inheritance when building classes, so if the classes in question aren't really going to be sharing behavior and don't really have an "is-a" relationship with a common base class, then they shouldn't share a common base class. 一般来说,在构建类时最好更喜欢组合而不是继承,所以如果有问题的类实际上不是共享行为而且实际上与公共基类没有“is-a”关系,那么他们不应该共享一个共同的基类。

  6. Prefer to have your base classes only share methods, not data. 希望您的基类只共享方法,而不是数据。 In other words, in an inheritance tree, it's best that only the leaves be instantiable. 换句话说,在继承树中,最好只有叶子是可实例化的。 There are various things like equals() which break down when you have base classes with actual data in them. 有各种各样的东西,比如equals() ,当你有基类和实际数据时会崩溃。 That's not to say that you can't do that - people do it all the time - but it can cause problems and is best avoided if it isn't needed. 这并不是说你不能这样做 - 人们总是这样做 - 但它可能会导致问题,如果不需要,最好避免。

  7. Prefer to override abstract methods. 更喜欢覆盖抽象方法。 Otherwise, in derived classes, you risk not calling the base class' method or totally changing what the method does. 否则,在派生类中,您可能无法调用基类的方法或完全改变方法的作用。

I'm sure that I could come up with more, but without really being familiar with your project, it's bound to be rather generic. 我确信我可以提出更多,但如果没有真正熟悉你的项目,它必然是相当通用的。 Out of the 3 options that you gave, I'd probably go with 2. 3 seems like you'd probably be creating a base class for unrelated classes, and 1 would result in Enemy being instantiatable, which you probably don't need and would definitely make it so that more than the leaves in your inheritance hierarchy would be instantiatable. 在你提供的3个选项中,我可能会选择2. 3看起来你可能正在为不相关的类创建一个基类,而1会导致Enemy被实例化,你可能不需要和肯定会使你的继承层次结构中的叶子不仅可以实例化。 You'll probably still end up with data in base classes with 2, but you're more likely to only be overriding abstract methods, and you'll have fewer problems with altered behavior in derived classes. 你可能仍然会在基类中使用2来获得数据,但是你更有可能只重写抽象方法,并且在派生类中改变行为的问题会更少。

A fourth option would be to use interfaces. 第四种选择是使用接口。

interface Enemy {

    public void draw();

    . . .

}

If you're just starting out, I would avoid your third option. 如果你刚刚开始,我会避免你的第三个选择。 Let the framework develop a little and see if there's a need for it. 让框架发展一点,看看是否需要它。

My rule of conduct is that a long as there are more than one class that share the same operations/data/methods/functionality, they should be extensions of the same abstract class. 我的行为准则是​​,只要有多个类共享相同的操作/数据/方法/功能,它们就应该是同一个抽象类的扩展。

So, if it was me doing it: 所以,如果是我这样做:

  • If ALL classes have something in common, have an top-level abstract class that gathers this functionality/fields/data in one place. 如果所有类都有共同点,请使用顶级abstract class ,在一个位置收集此功能/字段/数据。
  • If they don't, only those classes that actually have something in common should extend a lower-level abstract class . 如果他们不这样做,只有那些实际上有共同点的类应该扩展一个低级abstract class

If only methods are what the classes will have in common, interface s can be used as well. 如果只有方法是类的共同点,那么也可以使用interface However, I always find that sooner or later I see that the classes that implement the interface have the same private fields. 但是,我总觉得我迟早会看到实现interface的类具有相同的private字段。 At this point, I transform the interface to an abstract class that holds these private fields (to save on lines of code if nothing else). 此时,我将interface转换为包含这些私有字段的abstract class (如果没有其他内容则保存在代码行上)。

Just a small answer out of the book "More effective c++" page 271: 只是“更有效的c ++”第271页中的一个小答案:

"Make base classes abstract that are not at the end of a hierachy". “使基类不抽象,而不是在层次结尾”。 I'm too lazy to give you the whole chapert, but the autor lists some good reasons for that. 我懒得给你整个教练,但是autor列出了一些很好的理由。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM