多重继承的替代方案

时间:2015-03-26 10:03:30

标签: java inheritance design-patterns

在我第一次编程Java大约20年后,我希望我有多重继承。但我并不是因为我正在为这个具体问题寻找替代方案。

真正的应用程序是某种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*"

我尝试/想过的事情

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

我想我在这里面临一些重大的重写/重构。

因此,我可以将SportscarTruck设为装饰器,并使Firetruck同时使用两者。但这不起作用,因为doSteerdoDrive是从装饰对象中调用的,而装饰器只适用于来自"外部"的calles。所以我也必须将这些方法放在装饰器中,这也不是一个好主意。

因此,我可以使用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
    }
}

6 个答案:

答案 0 :(得分:10)

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

就像这张照片: Pattern strategy

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

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

因此,如果您只想改变所有与刀战斗的人的行为,您只需更改CombatCouteau中的代码。


字典

  • Personnage = Character
  • Guerrier = Warrior
  • Medecin =医生
  • Chirurgien =外科医生
  • Couteau = Knife
  • Pistolet = Gun
  • Marcher =步行
  • Courir = Run
  • Premier soin =急救
  • 操作=手术
  • Esprit combatif =好斗的心灵
  • 战斗,战斗=战斗,战斗
  • déplacement,sedéplacer= moving,to move
  • soin,soigner =愈合,愈合

答案 1 :(得分:4)

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,因此域对象的方法集应该足够丰富,以便在{{1方法。

答案 2 :(得分:4)

分割功能和状态

如果你不得不进行大规模的重构 - 解决这个复杂问题的一个好方法是分离状态和功能。只记得Java中的Object-Methods如何在幕后工作:基本上,对象 - 函数是一个静态函数,第一个参数是&#34;这个&#34;。 &#34;这&#34;参数是一个具有所需属性的状态对象。功能并不一定要与此绑定。

看起来像这样:

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对象组成您的状态:

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

答案 3 :(得分:2)

我不确定这个比喻是否足以给你一个好的答案..

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

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

使用功能界面可以减少麻烦,但是您不需要Java 8来解决问题。

答案 4 :(得分:1)

这个怎么样:

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

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
}

答案 5 :(得分:0)

前段时间我问过一个非常相似的问题......也许你会觉得它很有用:

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