如何设计具有基类中不可用功能的子类?

del*_*ber 5 java oop

例如,假设我有一个类Vehicle,我希望有一个子类ConvertibleVehicle,它有额外的方法,如foldRoof(),turboMode(),foldFrontSeats()等.我希望实例化如下

Vehicle convertible = new ConvertibleVehicle()
Run Code Online (Sandbox Code Playgroud)

所以我仍然可以访问常见的方法,如openDoor(),startEngine()等.我如何设计这样的解决方案?

为了澄清我的两个初始解决方案,我不满意的是:

  1. 有虚拟方法foldRoof(),turboMode(),foldFrontSeats()我只在ConvertibleVehicle中重写,让他们在其他子类中什么也不做
  2. 有抽象方法foldRoof(),turboMode(),foldFrontSeats()并强制每个子类提供一个实现,即使它在ConvertibleVehicle以外的所有实例中都是空白的

上面看起来有点复杂,因为它们都污染了基类,因为我添加了越来越多的子类,每个子类都有自己独特的功能

在阅读了一些回复之后,我的设计中可能存在某种类型的根本缺陷.假设我有一个类VehicleFleet,它接收车辆并指示他们驾驶如下:

public VehicleFleet(Vehicle[] myVehicles) {

    for (int i=0; i < myVehicles.length; i++) {
        myVehicles[i].drive();
    }
}
Run Code Online (Sandbox Code Playgroud)

假设这适用于车辆的几十个子类,但对于ConvertibleVehicle,我也想在驾驶之前折叠车顶.为此,我将VehicleFleet子类化如下:

public ConvertibleVehicleFleet(Vehicle[] myVehicles) {

    for (int i=0; i < myVehicles.length; i++) {
        myVehicles[i].foldRoof();
        myVehicles[i].drive();
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我有一个凌乱的函数foldRoof()卡在基类中,它不属于真正属于哪个,只有在ConvertibleVehicle的情况下才会被覆盖,并且在所有其他情况下都不会执行任何操作.解决方案有效,但似乎非常不优雅.这个问题是否适合更好的架构?

我正在使用Java虽然我希望找到一个可以在任何面向对象语言中工作的通用解决方案,并且我不需要依赖语言特定的怪癖

Kyl*_*ell 5

任何使用Vehicle的对象都不应该知道ConvertibleVehicle及其专用方法.在适当的松散耦合的面向对象设计中,Driver只知道Vehicle接口.驱动程序可能会在Vehicle上调用startEngine(),但是要由Vehicle的子类覆盖startEngine()来处理不同的实现,例如转动键和按下按钮.

考虑查看以下两个有助于解释这一概念的链接:http : //en.wikipedia.org/wiki/Liskov_substitution_principle http://en.wikipedia.org/wiki/Open/closed_principle

考虑发布一个现实世界的问题,您认为这会导致您在此处描述的困境,并且有人会非常乐意展示更好的方法.


Osc*_*Ryz 3

我在类似的情况下也这样做过。

选项A)

如果专用操作是与基本操作相同序列的一部分(例如,ConvertibleVehicle 需要折叠屋顶才能驾驶),则只需将专用操作放入基本操作中即可。

class Vehicle { 
     public abstract void drive();
}

class ConvertibleVehicle { 
     public void drive() { 
         this.foldRoof();
         .... // drive 
     }
     private void foldRoof() { 
         ....
     }
 }
Run Code Online (Sandbox Code Playgroud)

因此,驾驶车队的效果是其中一些人会在驾驶前折叠车顶。

 for( Vehicle v : vehicleFleet ) { 
      v.drive();
 }
Run Code Online (Sandbox Code Playgroud)

专用方法不会在对象公共接口中公开,但会在需要时调用。

选项B)

如果专用操作不是同一序列的一部分并且必须在某些“特殊”情况下调用,则让客户端的专用版本调用这些专用操作。警告,这不是那么纯粹,也不是低耦合,但是当两个对象(客户端和服务)都是由相同的“条件”或构建器创建时,大多数情况下都可以。

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 
             ...
         }
 }
Run Code Online (Sandbox Code Playgroud)

与前面的示例几乎相同,只是在这种情况下,foldRoof也是公共的。

不同之处在于我需要一个专门的客户:

// 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();
            }
       }
  }
Run Code Online (Sandbox Code Playgroud)

那个实例看起来有点丑陋,但它可能是由现代虚拟机内联的。

这里的要点是,只有专门的客户端知道并可以调用专门的方法。也就是说,只有RoofAwareFleetHandler可以在 ** ConvertibleVehicle** 上调用foldRoof()

最终代码不变...

 public class Main { 
     public static void main( String [] args ) { 
         FleetHandler fleetHandler = .....
         Vehicles [] fleet =  ....

          fleetHandler.handle( fleet );
      }
 }
Run Code Online (Sandbox Code Playgroud)

当然,我总是确保舰队处理程序和车辆数组兼容(可能使用abstrac工厂或构建器)

我希望这有帮助。