Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何设计具有基类中不可用特性的子类?_Java_Oop - Fatal编程技术网

Java 如何设计具有基类中不可用特性的子类?

Java 如何设计具有基类中不可用特性的子类?,java,oop,Java,Oop,例如,假设我有一个类Vehicle,我想要一个子类ConvertibleHicle,它有额外的方法,比如foldRoof()、turboMode()、foldFrontSeats()等等 Vehicle convertible = new ConvertibleVehicle() 所以我仍然可以使用常用的方法,如openDoor()、startEngine()等。我如何设计这样的解决方案 为了澄清我的两个初始解决方案,我对这两个方案都不满意: 有虚拟方法foldRoof()、turboMode

例如,假设我有一个类Vehicle,我想要一个子类ConvertibleHicle,它有额外的方法,比如foldRoof()、turboMode()、foldFrontSeats()等等

Vehicle convertible = new ConvertibleVehicle()
所以我仍然可以使用常用的方法,如openDoor()、startEngine()等。我如何设计这样的解决方案

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

  • 有虚拟方法foldRoof()、turboMode()、foldFrontSeats(),我只在ConvertibleHicle中重写这些方法,让它们在其他子类中什么都不做
  • 拥有抽象方法foldRoof()、turboMode()、foldFrontSeats(),并强制每个子类提供一个实现,即使它在除ConvertibleHicle之外的所有实例中都是空的
  • 上面的内容似乎有点复杂,因为它们都污染了基类,因为我添加了越来越多的子类,每个子类都有自己独特的函数

    在阅读了一些回复后,我的设计中可能存在一些基本缺陷。假设我有一个类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();
        }
    }
    
    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...
        }
    }