多重继承的替代方案

Alternative for multiple inheritance

经过 20 年左右的编程 Java 我第一次希望我有多重继承。但我没有,所以我正在寻找解决这个具体问题的替代方法。

真正的应用是某种 ERP 并且有些复杂,所以我尝试将这个问题转化为汽车。这只是到目前为止,但这是我能做的最好的。

让我们从描述汽车功能的界面开始:

public interface Car {

    public String accelerate();
    public String decelerate();

    public String steerLeft();
    public String steerRight();
}

现在我们有了一个基本的(但不是抽象的)实现:

public class BasicCar implements Car {

    protected final String name;

    public BasicCar( String name ) {

        this.name = name;
    }

    // In the real word this method is important and does lots of stuff like calculations and caching
    protected String doDrive( String how ) {
        return name + " is " + how + "ing";
    }
    @Override
    public String accelerate() {
        return doDrive( "accelerat" );
    }
    @Override
    public String decelerate() {
        return doDrive( "decelerat" );
    }


    // This method is important, too
    protected String doSteer( String where ) {
        return name + " is steering to the " + where;
    }
    @Override
    public String steerLeft() {
        return doSteer( "left" );
    }
    @Override
    public String steerRight() {
        return doSteer( "right" );
    }
}

在现实世界中,这本身就是 DAO 的外观。请注意,我需要 doDrivedoSteer 方法,因为这是完成一些计算、翻译和缓存等实际工作的地方。

还有一些更具体的实现:

public class Sportscar extends BasicCar {

    public Sportscar( String name ) {
        super( name );
    }

    // I need to call the super method
    @Override
    public String doDrive( String how ) {
        return super.doDrive( how ) + " fastly";
    }
}

public class Truck extends BasicCar {

    public Truck( String name ) {
        super( name, new BasicMotor(), new TruckySteerer() );
    }

    // I need to call the super method
    @Override
    public String doSteer( String where ) {
        return super.doSteer( where ) + " carefully";
    }
}

我现在能做的是:

Car mcqueen = new Sportscar( "McQueen" );
mcqueen.steerLeft() //-> "McQueen is steering left"
mcqueen.accelerate() // -> "McQueen is accelerating fastly"

Car mack = new Truck( "Mack" );
mack.steerLeft() //-> "Mack is steering left carefully"
mack.accelerate() // -> "Mack is accelerating"

我现在想做的是将这两者合而为一,共享它们的功能:

Car red = new Firetruck( "Red" );
red.steerLeft() //-> "Red is steering left *carefully*"
red.accelerate() // -> "Red is accelerating *fastly*"

我尝试/想到的

我可以将两者的代码复制并粘贴到一个 class。但这绝不是一个好主意。在实际应用程序中,这是相当多的代码,所以这是一个更糟糕的想法。

我想我在这里遇到了一些重大问题 rewrite/refactoring。

所以我可以让 SportscarTruck 成为 Decorator 并且 Firetruck 两者都使用。但这行不通,因为 doSteerdoDrive 是从装饰对象内部调用的,而装饰器仅适用于来自 "outside" 的调用。所以我也必须将这些方法放在装饰器中,这也不是一个好主意。

所以我可以使用 Java8 的新功能,并使 doSteerdoDrive 委托给这样的接口:

@FunctionalInterface
interface Driver {
    public String doDrive( String where );
}

并将其提供给 BasicCar 的构造函数 但是然后(除了变得非常复杂并且 final 出现一些其他相当讨厌的 java 问题之外)我无法再以一种好的方式访问 BasicCar 的状态。

所以目前我在这里有点迷路。任何好的想法都会受到赞赏。

编辑:举例说明这在现实世界中的样子:doSteer 组可能是这样的:

class ProductImpl {
    String getTitle(){
        return getField( "title" );
    }
    String getTeaser(){
        return getField( "teaser" );
    }
    String getField( String name ){
        Locale loc = configuredLocale();
        Map vars = configuredVarialbes();
        String value = getCached( name, loc );
        value = translateVariables( value );
        value = replaceVariables( value, vars );
        // and more
    }
}

Compositor 可以吗?它不能顺利地转化为汽车世界,但看起来适合您的任务:

public class FireTruck implements Car {

    public FireTruck( List<? extends Car> behaviors ) {
        this.behaviors = behaviors;
    }

    // I need to call the super method
    @Override
    public String doSteer( String where ) {
        String result = "":
        for (Car behavior : behaviors) result += behavior.doSteer(where);
        // post-process behavior some how to cut out infixes;
    }
}

我假设在您的真实应用程序中 doSteer 适用于某些域对象而不是 String,因此域对象的方法集应该足够丰富以在 do* 方法。

您可以使用模式策略来定义不同的行为并让子classes 实现它们。

就像这张图片:

这是法语(下面的词典),但非常不言自明:

  • Personnage 继承的所有 class 都是视频游戏中使用的角色。
  • 所有实现接口的classes(EspritCombatifDeplacementSoin)都在接口中定义了方法。他们是行为。
  • Personnageclass包含右侧定义的每个接口的对象。
  • 每个角色(Personnage 的子class)选择要实现的代码,例如在构造函数中执行 combat = new CombatCouteau();

因此,如果您只想更改所有拿刀打架的家伙的行为,只需更改 CombatCouteau 中的代码即可。


词典 :

  • 人物=人物
  • Guerrier = 战士
  • Medecin = 医生
  • Chirurgien = 外科医生
  • Couteau = 刀
  • 手枪 = 手枪
  • 游行者=步行
  • 库里尔 = 运行
  • Premier soin = 急救
  • 手术 = 手术
  • Esprit combatif = 好斗的头脑
  • 战斗,战斗=战斗,战斗
  • déplacement, se déplacer = 移动,移动
  • soin, soigner = healing, 治愈

这个怎么样:

public class SportsCar implements Car {
    private final BasicCar basicCar;
    public SportsCar( String name ) {
        this.basicCar = new BasicCar(name);
    }

    public SportsCar( BasicCar basicCar ) {
        this.basicCar = basicCar;
    }

    @Override
    public String accelerate() {
        return basicCar.accelerate() + " fastly";
    }
    @Override
    public String decelerate() {
        return basicCar.decelerate() + " fastly";
    }

    @Override
    public String steerLeft(){
        return basicCar.steerLeft() + " fastly";
    }

    @Override
    public String steerRight(){
        return basicCar.steerLeft() + " fastly";
    };
}

public class Truck implements Car {
    private final BasicCar basicCar;

    public Truck( String name ) {
        this.basicCar = new BasicCar(name);
    }

    public Truck( BasicCar basicCar ) {
        this.basicCar = basicCar;
    }

    @Override
    public String accelerate() {
        return basicCar.accelerate() + " carefully";
    }
    @Override
    public String decelerate() {
        return basicCar.decelerate() + " carefully";
    }

    @Override
    public String steerLeft(){
        return basicCar.steerLeft() + " carefully";
    }

    @Override
    public String steerRight(){
        return basicCar.steerLeft() + " carefully";
    };
}

现在 FireTruck class

public class FireTruck implements Car {
    Collection<Car> behaviors;
    BasicCar basicCar;

    public FireTruck(String name ) {
        this.behaviors = new ArrayList<String>();

        this.basicCar = new BasicCar(name);

        this.behaviors.Add(new SportsCar(this.basicCar));
        this.behaviors.Add(new Truck(this.basicCar));
    }

    @Override
    public String accelerate() {
        StringBuilder result = new StringBuilder();

        for(Car c : behaviors){
            result.append(c.accelerate());
        }

        //and if I wanted to do my own thing here as a 'FireTruck' I could, something like :
        String myOwnAction = basicCar.accelerate() + " sounding alarms and flashing red light";
        result.append(myOwnAction);
    }
    //same for other methods : decelerate, steerLeft and steerRight
}

拆分功能和状态

如果您无论如何都必须进行大型重构 - 解决这个复杂问题的一个好方法是将状态和功能分开。只要记住 Java 中的对象方法是如何在幕后工作的:本质上,对象函数是一个静态函数,第一个参数是 "this"。 "this" 参数是一个状态对象,具有您需要的所有属性。该功能不必绑定到此。

它可能看起来像这样:

class CarState {
  public float fuel;
  public String name;
}

class BasicCarFunctionality {
  public static void accelerate( CarState car ) {
    System.out.println( "accelerating" );
  }
}

class SportscarFunctionality {
  public static void drive( CarState car, String how ) {
    // Here you can reference Basic-Behaviour if you want
    BasicCarFunctionality.accelerate( car );
    System.out.println( car.name + " drive " + how );
  }
}

class TruckFunctionality {
  public static void steer( CarState car, String how ) {
    System.out.println( car.name + " steer " + how );
  }
}

interface SportsCar {
  void doDrive( String how );
}

interface Truck {
  void doSteer( String how );
}

class Firewagon implements SportsCar, Truck {
  private CarState car = new CarState();

  @Override
  public void doDrive( String how ) {
    SportscarFunctionality.drive( car, how );
  }

  @Override
  public void doSteer( String how ) {
    TruckFunctionality.steer( car, how );
  }
}

如果您的火车需要额外的 State-Attributes 而不是每辆 Car 都需要,您可以使用新的 State 对象组合您的状态:

class FirewagonExtraState {
  public float waterAmount;
  public boolean sirenActive;
}

// And ammend the Firewagon Class:
class Firewagon implements SportsCar, Truck {
  private CarState car                  = new CarState();
  private FirewagonExtraState firewagon = new FirewagonExtraState();

  @Override
  public void doDrive( String how ) {
    FirewagonFunctionality.activateHorn( firewagon );
    SportscarFunctionality.drive( car, how );
  }

  @Override
  public void doSteer( String how ) {
    TruckFunctionality.steer( car, how );
  }
}

我不确定这个类比是否正确,无法给您一个好的答案。

在Java中处理多重继承问题的最基本方法是使用接口,然后使用组合委托行为。

汽车和卡车都有转向和加速功能,但卡车不是汽车,不应仅仅因为某些实现是共享的就继承它的行为。将共享实现拉出到更有意义的 class(转向柱、传动系统等),然后从汽车和卡车委托给它。

使用函数式接口可以减少麻烦,但您不需要 Java 8 来解决问题。

前段时间我问了一个非常相似的问题...也许你会发现它有用:

A Collection of an Abstract Class (or something like that...)