如何在Java中建模可选继承

3

在以下情况下,是否存在可选继承的方法?

假设从这里开始:

enter image description here

注意:可能会有超过4个子类。
现在有一个可选的功能叫做“自主运输”,如下所示。

enter image description here

选项1:如上图所示,我可以让所有子类都继承“自主运输”,但我不想这样做。比如说,不能有自主列车。
选项2:不严格使用“自主运输”,而是根据需要让各个单独的类继承(可能带有接口),例如“汽车”、“自主汽车”、“卡车”、“自主卡车”。但这不是一个有吸引力的选项,因为子类远远超过4个。
选项3:可能,我可以使用依赖注入,在需要的地方注入“自主”功能,但我不能添加任何框架,如dagger等。
在Java中,还有其他干净的方法吗?基本上,如果“火车”不能自主,我不想在那里有任何相关的业务逻辑。

4
你可以使用装饰器模式。创建一个AutonomousTransport类,包装任何实现了Transport接口的类,并提供自主操作所需的功能。 - markspace
4
你无需使用继承来进行建模。在有机会的情况下,请优先使用组合而非继承。你可以拥有一个私有属性private AutonomousCapability autonomy,其中包含所有数据和行为。 - fps
1
与选项2类似,但为什么不让汽车、卡车和飞机继承自AutonomousTransport,而让火车直接继承自Transport呢?或者在您的模型中是否可能存在非自主驾驶汽车(这在选项1中并不完全清楚)? - Marcono1234
3个回答

3
为了简单明了起见:
选项1
abstract class Transport {
   foo();
} 

abstract class AutomonousTransport extends Transport {
   bar();
}

class Car extends AutonomousTransport {
   // inherits foo and bar
}

class Train extends Transport {
   //inherits only foo(), which is ok, since Train is still a transport 
}

采用这种方法,您需要决定继承哪个类以获得所需的功能。

这种方法的潜在缺点是,如果您有许多不同的功能(而在Java中您只能从一个类继承),那么就会出现问题。

为了克服这个问题,如果您只继承了功能(行为)而没有继承状态,请考虑使用带有默认方法的接口代替抽象类。这在某种程度上像其他语言中的Traits,但存在一些限制:

interface Transport {
   default foo () {...} 
}

interface AutonomousTransport extends Transport {
   default bar() {}
}
interface WheelSupportTransport extends Transport {
   default rotateWheelLeft() {...}
   default rotateWheelRight() {...}
}

class Car implements AutomonousTransport, WheelSupportTransport  {
   
}

class Train implements Transport {...}

选项2

abstract class Transport {
   foo() 
}

final class AutonomousStuff { // final to prohibit inheritance from it
   bar()
}

class Car extends Transport {
   AutonomousStuff autonomousStuff;
}

class Train extends Transport {
   // only foo
}

这种方法基于“优先使用组合而不是继承”的思想。将行为作为内部数据字段添加到所需的类中,并通过此字段获得所需的一组方法(功能)的访问权限。
abstract class Transport {
   foo()
}

abstract class AutonomousCapable extends Transport {
    private final Transport transport;
    public AutonomouseCapable(Transport transport) {
       this.transport  =  transport;
    }
    bar()

    foo() {
        transport.foo();
    }
    
}

class Car extends Transport {}

class Train extends Transport {} 


在创建汽车时,请使用new AutonomousCapable(new Car())
这遵循“装饰者模式”(又称包装器模式)的思想。您可以在运行时通过“装饰”对象(汽车)来增加一组额外的行为。

1
如果自主驾驶是可选的功能,则它实际上不是所有子类的广义属性。
我认为,应该有4个抽象类AbstractCar、AbstractTruck等。然后,每个抽象类都可以有两个具体的实现,比如AutonomousCar和NonAutonomousCar。一个是自主的,另一个不是。
你可以为自主类创建一个标记接口,叫做IAutonomousTransport。
当然,以上假设您不想在类中使用一个简单的名为isAutonomous的标志,并根据此处理您的功能。但这种方法不会多态。

1
你应该在这里使用装饰器模式
public interface Transport {
    void foo();
}

public class AutonomousTransport implements Transport {
    public void foo() {
        // autonomous transport implementation
    }
}

public class Car implements Transport {
    private final Transport delegate;
    
    public Car(Transport delegate) {
        this.delegate delegate;
    }
    
    public void foo() {
        if(delegate == null)
            // car transport implementation
        else
            delegate.foo();
    }
    
}

附言: 这段代码仅展示了简要的解决方案,实现这种模式有许多变体。

选项1: 如上图所示,我可以让所有子类继承“Autonomous Transport”,但我不想这样做。比如说,火车不能是自动驾驶的。

思考将 继承 替换为 委托。在大多数情况下,委托更可取。

选项2: 不严格使用 "Autonomous Transport" 作为中间层,而是根据需要继承个别类(可能带接口),如 "Car", "Autonomous Car", "Truck", "Autonomous Truck" 等。但这不是很有吸引力的选择,因为有更多的子类。

自动运输 对我来说更像是一种行为,而不是属性。我相信它不应该出现在 Transport 层次结构中。

选项3: 可能,我可以使用依赖注入,在需要时注入 "Autonomous" 能力,但我不能添加任何像 dagger 的框架。

依赖注入是一种模式而不是像Spring或Dagger那样的框架。如果您的类包含对另一个类的引用(无论是通过构造函数还是setter方法),实际上这已经是依赖注入了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接