多重继承的替代方案
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 的外观。请注意,我需要 doDrive
和 doSteer
方法,因为这是完成一些计算、翻译和缓存等实际工作的地方。
还有一些更具体的实现:
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。
所以我可以让 Sportscar
和 Truck
成为 Decorator 并且 Firetruck
两者都使用。但这行不通,因为 doSteer
和 doDrive
是从装饰对象内部调用的,而装饰器仅适用于来自 "outside" 的调用。所以我也必须将这些方法放在装饰器中,这也不是一个好主意。
所以我可以使用 Java8 的新功能,并使 doSteer
和 doDrive
委托给这样的接口:
@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(
EspritCombatif
、Deplacement
、Soin
)都在接口中定义了方法。他们是行为。
Personnage
class包含右侧定义的每个接口的对象。
- 每个角色(
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...)
经过 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 的外观。请注意,我需要 doDrive
和 doSteer
方法,因为这是完成一些计算、翻译和缓存等实际工作的地方。
还有一些更具体的实现:
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。
所以我可以让 Sportscar
和 Truck
成为 Decorator 并且 Firetruck
两者都使用。但这行不通,因为 doSteer
和 doDrive
是从装饰对象内部调用的,而装饰器仅适用于来自 "outside" 的调用。所以我也必须将这些方法放在装饰器中,这也不是一个好主意。
所以我可以使用 Java8 的新功能,并使 doSteer
和 doDrive
委托给这样的接口:
@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(
EspritCombatif
、Deplacement
、Soin
)都在接口中定义了方法。他们是行为。 Personnage
class包含右侧定义的每个接口的对象。- 每个角色(
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...)