Java中避免多重继承不足的方法

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

我在做游戏。我有一个实体类,它是一对x,y坐标,宽度和高度

我想将这个实体类子类化为许多新类,以添加功能组的组合。我想添加几个“组”功能:

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.a
registerNewMove(可移动操作)
,您甚至可以在需要时在运行时交换具体的实现(策略模式)。

也许从完全不同的角度看待您的问题会很有用。您是否考虑过,与其将功能构建到对象的层次结构中,不如将功能构建到对象的初始化中

首先,尝试以下方法:

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);
}