Как мне обойти эту проблему множественного наследования? - PullRequest
5 голосов
/ 06 мая 2020

Я не могу найти решение этой проблемы наследования. Я работаю над программой, которая будет хранить информацию о небесных телах. У меня есть абстрактный суперкласс, Body, от которого должны унаследоваться все остальные небесные тела. Теперь я хочу, чтобы некоторые тела имели реализацию по умолчанию для хранения информации об орбитальных телах; некоторые тела должны быть Orbitable, а некоторые - Orbital. например, звезда - это только orbitable, планеты и луны - это orbitable и orbital, а астероид - только orbital.

public abstract class Orbital {

    Body host;

    protected double avgOrbitalRadius;
    protected double orbitalPeriod;

    public double getOrbitalRadius(){return this.avgOrbitalRadius;}
    public double getOrbitalPeriod(){return this.orbitalPeriod;}

}
public abstract class Orbitable {

    List<Body> satellites = new ArrayList<>();

    public void addSatellite(Body sat){
        satellites.add(sat);
    }

    public boolean hasSatellite(Body sat){
        for(Body body : satellites){
            if(sat.equals(body)) return true;
        }
        return false;
    }

    public boolean hasSatellite(String satName){
        for(Body body : satellites){
            if(satName.equals(body.getName())) return true;
        }
        return false;
    }

    public Body getSatellite(String satName){
        for(Body body : satellites){
            if(satName.equals(body.getName())) return body;
        }
        return null;
    }
}

Мне нужно, чтобы объекты были может унаследовать одну, обе или ни одну из вышеперечисленных реализаций (плюс суперкласс Body, который описывает основу любого небесного тела). Я пробовал использовать интерфейсы с методами по умолчанию, но ключевая проблема заключается в том, что реализация включает в себя чтение или изменение состояния объекта, что не может быть реализовано с интерфейсами, потому что все переменные в интерфейсе неявно статичны c.

Я также просмотрел этот и этот пост об очень похожих проблемах, но наследование состояния вызывает у меня горе.

Итак, как я могу решить эту множественную проблема наследования? Это вообще возможно в Java? Есть ли другие конструкции, которые могут обойти эту проблему? Спасибо.

Ответы [ 3 ]

5 голосов
/ 06 мая 2020

enter image description here Позвольте мне подойти к этой проблеме классического c объектно-ориентированного шаблона с некоторой визуальной помощью.

У меня было бы два интерфейса, а именно:

Orbitable
Orbital

Тогда базовый класс Body. Вы можете сделать его конкретным или абстрактным в зависимости от деталей вашей реализации. Например, вы можете добавить поведение типа есть ли в нем атмосфера? в форме метода hasAtmosphere(). Сначала это может быть abstract, а затем ваши реализующие классы переопределят это.

Затем у меня будет два дополнительных подкласса, расширяющих Body и реализующих каждый интерфейс (помните, что интерфейс разделяет ответственность, предоставляя контракты для поведения)

BaseOrbitable
BaseOrbital
BaseOrbitalOrbitable

Наконец, несколько конкретных примеров реализации (подклассов):

public class Earth extends Planet {
}

или

public class Ceres extends Asteroid {
}

или

public class Sirius extends BaseOrbitable {
}
5 голосов
/ 06 мая 2020
  1. Создание интерфейсов Orbitable и Orbital, которые определяют (но, очевидно, не реализуют) методы для манипуляций с состоянием, которые вы хотите иметь.

Создание трех (абстрактных) классов

  • OrbitableBody extends Body implements Orbitable
  • OrbitalBody extends Body implements Orbital
  • OrbitableOrbitalBody extends Body implements Orbitable, Orbital

И заставить эти три класса реализовать методы из интерфейсов.


Сделайте так, чтобы ваши небесные тела расширили подходящий класс из четырех: Body, OrbitableBody, OribtalBody или OrbitableOrbitalBody.

Тогда все они будут телом, будут реализовывать правильные интерфейсы и унаследовать реализации по умолчанию для методов, определенных интерфейсом.

4 голосов
/ 06 мая 2020

Йоханнес Х. ответ относительно прост и может быть хорошим решением. Но есть проблема с дублированием кода. Вместо этого я предлагаю думать о Orbital и Orbitable как о чем-то, что есть у ваших небесных тел, и использовать композицию и делегирование:

class Body {
}

interface Orbital {
    public double getOrbitalRadius();
}

interface Orbitable {
    public void addSatellite(Body sat);
}

// Default common implementation, shouldn't be abstract
class OrbitalImpl implements Orbital {
    protected double avgOrbitalRadius;
    public double getOrbitalRadius(){return this.avgOrbitalRadius;}
    // ...
}

class OrbitableImpl implements Orbitable {
    List<Body> satellites = new ArrayList<>();
    public void addSatellite(Body sat){satellites.add(sat);}
    //...
}

class OrbitableOrbitalBody extends Body implements Orbitable, Orbital {
    Orbitable orbitable;
    Orbital orbital;

    public OrbitableOrbitalBody() {
        orbitable = new OrbitableImpl();
        orbital = new OrbitalImpl();
    }

    public OrbitableOrbitalBody(Orbitable orbitable, Orbital orbital) {
        this.orbitable = orbitable;
        this.orbital = orbital;
    }

    @Override
    public double getOrbitalRadius() {
        return orbital.getOrbitalRadius();
    }

    @Override
    public void addSatellite(Body sat) {
        orbitable.addSatellite(sat);
    }
} 

class OrbitableBody extends Body implements Orbitable {
    Orbitable orbitable;

    // Use default implementation
    public OrbitableBody() {
        orbitable = new OrbitableImpl();
    }

    // If needed use orbitable that behaves differently
    public OrbitableBody(Orbitable orbitable) {
        this.orbitable = orbitable;
    }

    // delegate to orbitable
    @Override
    public void addSatellite(Body sat) {
        orbitable.addSatellite(sat);
    }
}

// Same as Orbitable
//class OrbitalBody extends Body implements Orbital {
...