Java中避免多重继承不足的方法
我在做游戏。我有一个实体类,它是一对x,y坐标,宽度和高度 我想将这个实体类子类化为许多新类,以添加功能组的组合。我想添加几个“组”功能:Java中避免多重继承不足的方法,java,oop,inheritance,multiple-inheritance,Java,Oop,Inheritance,Multiple Inheritance,我在做游戏。我有一个实体类,它是一对x,y坐标,宽度和高度 我想将这个实体类子类化为许多新类,以添加功能组的组合。我想添加几个“组”功能: Moveable { public float getVelocityX(); public void setVelocityX(float velocityX); public float getVelocityY(); public void setVelocityY(float velocityY); publi
Moveable {
public float getVelocityX();
public void setVelocityX(float velocityX);
public float getVelocityY();
public void setVelocityY(float velocityY);
public void moveRelative(float dx, float dy);
public void setPosX(float posX);
}
及
及
。。。以及更多(可更新、可绘制、可碰撞)
某些实体可以旋转/绘制,但不能移动/碰撞/更新/调整大小:“静态”不可触摸的对象,例如背景动画(云、太阳、星星)
一些实体可以移动/旋转/绘制/更新/碰撞/调整大小:玩家角色和怪物
某些实体只能绘制:游戏的HUD
某些实体可以移动/绘制/更新/碰撞,但不能旋转/调整大小:例如枪射击
正如您所看到的,我需要上述特性的组合,并且可以通过一些多重继承很容易地解决
为什么不使用您要求的接口呢?因为这会让我为所有方法复制/粘贴大量代码,而创建新类型的实体类将永远引入复制/粘贴
您可能会问,为什么我不创建一个可以完成所有任务的子类,然后重写不应该使用空方法的方法,或者在无效的方法中抛出UnsupportedOperationException?这仍然是复制/粘贴,即使每个方法中只有一行。一个只能move()
的实体最终可能会有50个方法带有“throw new UnsupportedOperationException();”,这是不可接受的
如果您建议使用适配器模式,我希望您能发布一个适配器模式如何执行类似操作的示例(我已经对此进行了讨论)
如果您建议合成,请解释我将如何访问受保护的字段
谢谢 如果所有子类的代码都相同,那么它可能在超类中。你可以这样做:
public final move() {
if (!isMovable()) {
throw new UnsupportedOperationException("This entity can't be moved");
}
// TODO implement the actual move
}
public abstract boolean isMovable();
如果希望有接口来判断实体是否可移动,甚至可以在超类中实现isMovable()方法:
public final boolean isMovable() {
return this instanceof Movable;
}
正如罗曼在评论中所说: Java确实允许
接口的多重继承。如果您只需要继承is-a关系和一组有保证的方法来与对象通信,那么这将非常有效
只能从继承单个类(抽象或非抽象)(以及所需的多个接口)。这是为了避免“钻石继承”的复杂性——通过搜索该短语了解更多信息。如果确实需要重用来自多个源的逻辑,通常的解决方法是在实用程序类中定义额外的共享行为,并将类委托给该类的实例。是的,这是一个has-a而不是is-a关系,但是由于is-a可以抽象为一个接口,因此您可以获得组合行为的期望最终外观。(与diamond继承不同,这迫使您明确决定要调用哪个实现。)根据契约,您必须实现接口中定义的方法。然而,在这些方法中,你可以做任何你喜欢的事情。在这个简单的示例中,为每个实例创建了Moveable
和Rotatable
的进一步实现类。对相应操作的请求将转发给实现
这样,您就可以与多个对象共享同一个实现。操作本身甚至能够管理其状态,而不是委托类
public SomeClass implements Moveable, Rotatable
{
// the implementation provide a concrete implementation
// you can even exchange the implementation with new ones
private Moveable movaAction = new MoveImpl();
private Rotatable rotateAction = new RotateImpl();
public float getVelocityX()
{
return moveAction.getVelocityX();
}
public void setVelocityX(float velocityX)
{
moveAction.getVelocityX(velocityX);
}
...
public void setAngularVelocity(float degreesPerSecond)
{
rotateAction.setAngularVelocity(degreesPerSecond);
}
...
}
如果您进一步定义f.e.aregisterNewMove(可移动操作)
,您甚至可以在需要时在运行时交换具体的实现(策略模式)。也许从完全不同的角度看待您的问题会很有用。您是否考虑过,与其将功能构建到对象的层次结构中,不如将功能构建到对象的初始化中
首先,尝试以下方法:
static class Thing {
int x;
int y;
float w;
float h;
float angle;
}
enum Op {
Translate{
@Override
void op (Thing it, Thing op) {
it.x += op.x;
it.y += op.y;
}
},
Rotate{
@Override
void op (Thing it, Thing op) {
it.angle += op.angle;
}
},
Scale{
@Override
void op (Thing it, Thing op) {
it.w *= op.w;
it.h *= op.h;
}
};
abstract void op(Thing it, Thing op);
}
现在您有了一个可以操作的基类东西
。构建合法操作现在变成了一个数据处理问题,而不是层次结构问题。您所描述的内容可以使用接口而不是子类轻松实现。另一种方法是使用对象组合而不是继承。如果一个接口有多个使用相同“代码”的实现,您可以将代码重构为动作f.e.,然后将实现传递给具体动作(类似于命令模式),您可以随意演示如何实现。看起来使用“has-a”关系(has-a位置、has-a大小)会容易得多而不是试图用继承来做。这样做的问题是所有实例都有相同的方法,在那些云不可碰撞的实例中,你建议我用这些方法做什么?抛出不支持的概念?在我的问题中,我已经讨论过这个解决方案。您可能有兴趣了解一下Java8(我想它将在下个月发布)您可以实现一种安全的多重继承形式,因为接口是允许的,因此接口可以有方法体,除非实现类提供自己的实现。这将在所有6个接口的所有方法中添加3行“throw new”,这6个接口目前是“设计阶段”中的19个方法。我希望它能长很多,那又怎样?他们在一个班里。如果您想要一个包含19个方法的类,那么您不应该害怕有19*3行琐碎的代码。您确定所有这些代码都应该在实体本身中,而不是在EntityOver类中吗?你确定你真的想要子类吗?如果一个太阳与一颗恒星的区别仅仅在于它的属性值,为什么不让一切都成为实体的一个实例,具有不同的状态呢?BackgroundEntity太阳=新的BackgroundEntity(xxx);BackgroundEntity star=新背景
public SomeClass implements Moveable, Rotatable
{
// the implementation provide a concrete implementation
// you can even exchange the implementation with new ones
private Moveable movaAction = new MoveImpl();
private Rotatable rotateAction = new RotateImpl();
public float getVelocityX()
{
return moveAction.getVelocityX();
}
public void setVelocityX(float velocityX)
{
moveAction.getVelocityX(velocityX);
}
...
public void setAngularVelocity(float degreesPerSecond)
{
rotateAction.setAngularVelocity(degreesPerSecond);
}
...
}
static class Thing {
int x;
int y;
float w;
float h;
float angle;
}
enum Op {
Translate{
@Override
void op (Thing it, Thing op) {
it.x += op.x;
it.y += op.y;
}
},
Rotate{
@Override
void op (Thing it, Thing op) {
it.angle += op.angle;
}
},
Scale{
@Override
void op (Thing it, Thing op) {
it.w *= op.w;
it.h *= op.h;
}
};
abstract void op(Thing it, Thing op);
}