抽象基类还是类?
对于我的学期项目,我和我的团队应该制作一个 .jar 文件(库,不可运行),其中包含游戏开发框架并演示 OOP 的概念。它应该是一个框架,另一个团队应该使用我们的框架,反之亦然。所以我想知道我们应该如何开始。我们想到了几种方法:
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 {
...
}
我应该使用哪个?或者有更好的方法吗?
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. 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
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
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
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
好吧,如果不深入了解你在做什么,就很难确定,即使这样,它也是相当主观的。但是,有一些需要考虑的事情可以告诉您。
他们是否要实际实例化一个敌人,或者所有敌人真的都需要是派生类型吗? 而不是派生类型,那么它可能应该是一个接口或抽象类。
如果您希望在基类中提供实际行为,那么显然它需要是一个类而不是接口。
如果您希望在基类中提供实际行为,那么显然它需要是一个类而不是接口。
需要为 API 提供但对您提供任何实现没有任何意义的方法应该是抽象的。
在基类中实现更有意义的方法应该在基类中实现。如果重写它们没有什么意义,那么就将它们设为最终的。
只有当类确实共享行为或者您需要能够在代码中的某个位置相同地对待它们时,让类共享一个公共基类才真正有意义。如果它们并不是真的那么相似,那么它们可能不应该共享一个基类。例如,如果敌人和玩家都应该是可显示的,那么拥有一个处理它们的显示功能的公共基类可能是有意义的。但是,如果 Enemy 是可显示的东西,而 Player 是一个更抽象的概念(例如游戏的控制器)并且不可显示,那么它们共享基类可能就没有意义。一般来说,在构建类时最好选择组合而不是继承,因此如果所讨论的类不会真正共享行为并且与公共基类没有真正的“is-a”关系,那么它们不应该共享一个公共基类。
希望您的基类仅共享方法,而不共享数据。换句话说,在继承树中,最好只有叶子是可实例化的。当您的基类中包含实际数据时,有多种类似
equals()
的东西会崩溃。这并不是说您不能这样做 - 人们总是这样做 - 但它可能会导致问题,如果不需要,最好避免这样做。更喜欢重写抽象方法。否则,在派生类中,您可能会面临不调用基类的方法或完全更改该方法的功能的风险。
我确信我可以想出更多,但如果没有真正熟悉您的项目,它肯定会相当通用。在你提供的 3 个选项中,我可能会选择 2。3 看起来你可能会为不相关的类创建一个基类,1 会导致 Enemy 可实例化,你可能不需要它肯定会使得继承层次结构中的叶子以上的部分都可以实例化。您可能仍然会在带有 2 的基类中得到数据,但您更有可能只重写抽象方法,并且在派生类中更改行为时遇到的问题会更少。
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.
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.
If you're looking to provide actual behavior in your base class, then obviously it needs to be a class rather than an interface.
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.
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.
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. 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. 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.
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. 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.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. 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.
第四种选择是使用接口。
如果你刚刚开始,我会避免你的第三个选择。让框架发展一点,看看是否有需要。
A fourth option would be to use interfaces.
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:
abstract class
that gathers this functionality/fields/data in one place.abstract class
.If only methods are what the classes will have in common,
interface
s can be used as well. However, I always find that sooner or later I see that the classes that implement theinterface
have the sameprivate
fields. At this point, I transform theinterface
to anabstract class
that holds these private fields (to save on lines of code if nothing else).只是《更有效的 c++》第 271 页中的一个小答案:
“使不在层次结构末尾的基类抽象”。我懒得给你整章,但作者列出了一些很好的理由。
Just a small answer out of the book "More effective c++" page 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.