Java 如何设计具有基类中不可用特性的子类?
例如,假设我有一个类Vehicle,我想要一个子类ConvertibleHicle,它有额外的方法,比如foldRoof()、turboMode()、foldFrontSeats()等等Java 如何设计具有基类中不可用特性的子类?,java,oop,Java,Oop,例如,假设我有一个类Vehicle,我想要一个子类ConvertibleHicle,它有额外的方法,比如foldRoof()、turboMode()、foldFrontSeats()等等 Vehicle convertible = new ConvertibleVehicle() 所以我仍然可以使用常用的方法,如openDoor()、startEngine()等。我如何设计这样的解决方案 为了澄清我的两个初始解决方案,我对这两个方案都不满意: 有虚拟方法foldRoof()、turboMode
Vehicle convertible = new ConvertibleVehicle()
所以我仍然可以使用常用的方法,如openDoor()、startEngine()等。我如何设计这样的解决方案
为了澄清我的两个初始解决方案,我对这两个方案都不满意:
public VehicleFleet(Vehicle[] myVehicles) {
for (int i=0; i < myVehicles.length; i++) {
myVehicles[i].drive();
}
}
public ConvertibleVehicleFleet(Vehicle[] myVehicles) {
for (int i=0; i < myVehicles.length; i++) {
myVehicles[i].foldRoof();
myVehicles[i].drive();
}
}
public vehicleleft(车辆[]我的车辆){
对于(int i=0;i
假设这适用于几十个子类的车辆,但对于敞篷车,我还想在驾驶前折叠车顶。为此,我将VehicleFleet分类如下:
public VehicleFleet(Vehicle[] myVehicles) {
for (int i=0; i < myVehicles.length; i++) {
myVehicles[i].drive();
}
}
public ConvertibleVehicleFleet(Vehicle[] myVehicles) {
for (int i=0; i < myVehicles.length; i++) {
myVehicles[i].foldRoof();
myVehicles[i].drive();
}
}
公共敞篷车(车辆[]我的车辆){
对于(int i=0;i
这就给我留下了一个凌乱的函数foldRoof(),它被困在基类中,而基类并不真正属于它,它只在ConvertibleHicle中被重写,在所有其他情况下都不做任何事情。这个解决方案可行,但似乎很不雅观。这个问题是否适用于更好的体系结构
我正在使用Java,尽管我希望可以找到一个通用的解决方案,它可以在任何面向对象的语言中工作,并且我不需要依赖于特定于语言的怪癖,这正是子类化所做的:添加基类中不存在的功能
class MyVehicle : public Vehicle {
public:
void MyNewFunction()
...
有两种(实际上只是)不同的继承方式:公共继承和私有继承,分别反映了Is-A和Has-A的关系。使用公共继承,您可以直接向类添加内容。如果我的类Animal有Eat()和Walk()方法,我可以创建一个名为Cat的子类,它的方法是Purr()。然后,猫有吃、走和咕噜的公共方法
但是,在基于LinkedList的堆栈的情况下,我可以说堆栈内部有一个LinkedList。因此,我不公开基类的任何特性,我将它们保留为私有,并且必须显式地提供我选择的任何公共特性。一个列表可能有一个Insert()方法,但对于堆栈,我限制了实现并将其设置为Push()。没有公开以前的公共方法
在C++中,这是由基类前的访问修饰符定义的。上面,我使用的是公共继承。在这里,我使用私有继承:
class MyVehicle : private Engine {
这反映了我的车有一个发动机
最终,子类化会获取基类中所有可用的内容,并向其中添加新内容
编辑:
有了这些新信息,您似乎真正在寻找的是,正如先前(被否决的)评论所述的接口。这是继承粒度的一个大问题。C++最大的抱怨之一是它实现了多重继承(一种实现这一点的选项)。您能具体说明您使用的是什么语言,以便我们能给出正确的建议吗?这是一个好问题。这意味着您拥有(或期望拥有)要求车辆(例如)foldRoof()的代码。这是一个问题,因为大多数车辆不应该折叠车顶。只有知道它正在处理一个ConvertibleHicle的代码才应该调用该方法,这意味着它是一个只应该在ConvertibleHicle类中的方法。这样比较好,;只要您尝试调用Vehicle.foldRoof(),编辑器就会告诉您这无法完成。这意味着您需要整理代码,以便知道您正在处理一个可转换车辆,或者取消foldrof()调用。任何使用车辆的对象都不应该知道可转换车辆及其专用方法。在适当的松耦合面向对象设计中,驾驶员只知道车辆接口。驾驶员可能会在车辆上调用startEngine(),但这取决于车辆的子类来覆盖startEngine(),以处理不同的实现,例如转动钥匙和按下按钮 考虑回顾以下两个有助于解释这一概念的链接:
考虑发布一个现实世界中的问题,你觉得这会导致你在这里描述的困境,有人会非常乐意展示一个更好的方法。我认为大多数人都没有抓住德尔塔问题的重点。在我看来,他/她并不是在问什么是遗产。他/她询问的是实现基类不适合的功能的子类,以及由此产生的混乱。也就是说,将特定方法/功能推到层次结构链上,或者要求子类实现非自然匹配功能的契约
还有一个问题是,在任何情况下都能像对待子类一样对待基类是否有价值(以避免强制转换并交替使用它们)*编辑——这被称为Liskov替换原则(谢谢你提醒我,Kyle)。车辆用户如何知道它是一辆敞篷车?要么他们需要动态强制转换以确保它是正确的,要么您提供了一个方法i
class Vehicle {
public void drive() {
....
}
}
class ConvertibleVehicle extends Vehicle {
// specialized version may override base operation or may not.
public void drive() {
...
}
public void foldRoof() { // specialized operation
...
}
}
// Client ( base handler )
public class FleetHandler {
public void handle( Vehicle [] fleet ) {
for( Vehicle v : fleet ) {
v.drive();
}
}
}
// Specialized client ( sophisticate handler that is )
public class RoofAwareFleetHandler extends FleetHandler {
public void handle( Vehicle [] fleet ) {
for( Vehicle v : fleet ) {
// there are two options.
// either all vehicles are ConvertibleVehicles (risky) then
((ConvertibleVehicles)v).foldRoof();
v.drive();
// Or.. only some of them are ( safer ) .
if( v instenceOf ConvertibleVehicle ) {
((ConvertibleVehicles)v).foldRoof();
}
v.drive();
}
}
}
public class Main {
public static void main( String [] args ) {
FleetHandler fleetHandler = .....
Vehicles [] fleet = ....
fleetHandler.handle( fleet );
}
}
public class Vehicle {
protected void prepareToDrive() {
// no-op in the base class
}
protected abstract void go();
public final void drive() {
prepareToDrive();
go();
}
}
public class ConvertableVehicle extends Vehicle {
// override setup method
protected void prepareToDrive() {
foldRoof();
}
protected void go() {
// however this works
}
protected void foldRoof() {
// ... whatever ...
}
}
public class VehicleFleet {
// other stuff...
public void add(Vehicle vehicle) {
// adds to collection...
}
}
public class TruckVehicle extends Vehicle {
// truck-specific methods...
}
public class VehicleFleet<T extends Vehicle> {
void add(T vehicle) {
// add to collection...
}
}
public interface VehicleFleetBase {
Vehicle find(String name);
// note that 'add' methods or methods that pass in vehicles to the fleet
// should *not* be in here
}
public class VehicleFleet<T extends Vehicle> {
void add(T vehicle) {
// add to collection...
}
Vehicle find(String name) {
// look up vehicle...
}
}