【问题标题】:Learning interfaces and hierarchies, where to place certain variables and methods?学习接口和层次结构,在哪里放置某些变量和方法?
【发布时间】:2016-04-22 16:41:59
【问题描述】:

因此,作为汽车租赁系统的一部分,我需要编写类来表示大型和小型汽车,它们之间的区别在于它们具有不同大小的油箱并以不同的速率消耗燃料。目前我的方法是有一个接口 Car,由抽象类 AbstractCar 实现,它由两个具体类 SmallCar 和 LargeCar 扩展。然而,这是我第一次使用接口和抽象类(我们只是在课堂上介绍它们,这个作业旨在评估我们对它们的了解),我不知道在什么类中放置什么。

fill方法的实现完全一样,只需要引用FUEL_CAPACITY的正确值,所以感觉应该是在AbstractCar类中实现了这些方法,后来不知道怎么弄参考正确的 FUEL_CAPACITY 值。字段fuelLevel显然也被所有汽车持有,所以我觉得我应该在AbstractCar中声明它,但是如果不删除它的隐私,我就不能从子类中访问它。

谁能帮我弄清楚我做错了什么或对接口和继承有误解?我一直在考虑的一件事是生成一个枚举 CarType,让 AbstractCar 将 CarType 作为一个字段,并且所有实现都在 AbstractCar 类中完成,使用 if 语句切换到正确的 FUEL_CAPACITY 值,并简单地使用 SmallCar 和 LargeCar 作为构造函数或没有太多甚至没有任何实际实现的工厂类。

提前感谢您提供的任何帮助' 但不一定是正确或最优雅的解决方案。

【问题讨论】:

  • 您可以将逻辑与值一起传输到AbstractCar。然后只需在SmallCar 和LargeCar 的委托人中设置这些值。这将是一种方法。就像您指出的那样,您始终必须在父类中具有通用逻辑。您想避免重复代码。然后你只需要确保你在构造函数中设置了不同的值。如果您知道固定值,您甚至可以省略给SmallCar 或LargeCar 提供参数,而只需在构造函数内的super() 调用中设置这些固定值。

标签: java inheritance interface abstract class-hierarchy


【解决方案1】:

您可以使用您指出的值将逻辑传输到 AbstractCar。然后只需在 SmallCar 和 LargeCar 的构造函数中设置这些值。这将是一种方法。就像您指出的那样,您始终必须在父类中具有通用逻辑。你想避免重复的代码。然后你只需要确保你在构造函数中设置了不同的值。如果您知道固定值(正如您在给定示例中所做的那样),您甚至可以省略为 SmallCar 或 LargeCar 构造函数提供参数,而只需在构造函数内的 super() 调用中设置这些固定值。

这是我的解决方案的实现。

接口Car,我删除了getFuelMethod()方法,因为必须保护访问级别:

public interface Car {

  RegistrationNumber getRegistration();

  int getFuelCapacity();

  // int getFuelLevel(); this can not be implemented
  // all methods in an interface are PUBLIC
  // so you have to lower the access level by removing it from the interface

  // HERE goes the rest of the method signatures

}

}

抽象类AbstractCar:

public abstract class AbstractCar implements Car {
  // this is the common variable
  // that is why we save it in the parent class
  private int fuelCapacity;

  private int fuelLevel;

  // we forward the value to the parent constructor with the super call
  public AbstractCar(int fuelCapacity) {
    this.fuelCapacity = fuelCapacity;
    // I set the value to 0 for the start, but
    // you can also pass the value to the super call,
    // same as fuelCapacity - it is up to you
    this.fuelLevel = 0;
  }

  // The getters and setter allow us to retrieve the values
  // from the abstract class through capsulation!

  // here we have the getter to be able to retrieve the value from SmallCar and LargeCar
  public int getFuelCapacity() {
    return.fuelCapacity;
  }

  public void setFuelCapacity(int fuelCapacity) {
    this.fuelCapacity = fuelCapacity;
  }

  protected int getFuelLevel() {
    return fuelLevel;
  }

  protected void setFuelLevel(int fuelLevel) {
    this.fuelLevel = fuelLevel;
  }

  // HERE goes the rest of the code

}

这是SmallCar 的实现:

public class SmallCar extends AbstractCar {

  private static final int FUEL_CAPACITY = 45;

  public SmallCar() {
    // we set the value in the parent class
    super(FUEL_CAPACITY);
  }

  public int drive() {
    // HERE goes the logic for drive for SmallCar. Same method is needed
    // in the LargeCar class, because the logic differes.
  }

  // HERE goes the rest of the code

}

【讨论】:

  • 谢谢,我认为这是针对我的情况给出的最佳答案,因为燃料容量值是恒定的,小型为 45,大型为 65。在接口中将这两个常量声明为 SMALL_FUEL_CAPACITY 和 LARGE_FUEL_CAPACITY 或在具体类中分别声明是否会更好?另外,我意识到 drive() 的实现确实略有不同。所以驱动方法需要在具体类中实现,还需要写入抽象类中的fuelLevel。任何想法如何解决这个问题?再次感谢!
  • 由于燃料容量是一个常数,所以我建议在具体类中声明它,并按照我的建议在构造函数的super 调用中传递值。关于drive 方法,你是对的。如果实现不同,但你的方法签名还是一样的,那么建议在抽象类中声明一个抽象方法。
  • 但是我现在看到您已经在接口中声明了该方法,这是一种更好的方法。所以你有义务在具体类中实现这个功能 - AbstractCar 不能被实例化,这意味着它不必实现drive 方法。
  • 关于fuelLevel,如果将其存储为值,则将声明放入抽象类中。如果每次调用getFuelLevel都计算这个,那么就不需要在抽象类中声明变量,只需在具体类上实现方法即可。希望我设法解释得足够好。从您的问题来看,这个值似乎也代表了一个状态,所以我将更新我的解决方案并将状态添加到抽象类中。
  • 感谢您的扩展。我的问题是“fill”方法需要更改“fuelLevel”变量,现在这两个变量都在“AbstractCar”中声明了。但是,“drive”需要在具体类中实现,还需要更改“fuelLevel”变量。现在“fuelLevel”位于“AbstractCar”中并且是私有的,我不能再从具体类中更改它,因为我需要实现“drive”。所以我想知道正确的解决方法是什么?看到 Java 没有访问修饰符只能为子类提供访问权限
【解决方案2】:

如果您只想对类用户隐藏FUEL_CAPACITY 而不是对其他开发人员隐藏,您可以在AbstractCar 中将其声明为protected,并在儿童班。此外,我会在返回此值的 AbstractCar 中声明一个 getter 方法 getCapacity()。

【讨论】:

    【解决方案3】:

    如果你的Capacity只是Car的一个属性(只有数据),使用@Jernej K approach,但是如果计算容量可能有一些逻辑,使用这个:

    最好的方法是使用抽象方法。你在你的抽象类中放了一个abstract Integer getCapacity();的方法

    public abstract class AbstractCar implements Car {
    
        private final RegistrationNumber registration;
        private boolean isRented;
    
        AbstractCar() {
            this.registration = RegistrationNumber.getInstance();
        }
    
        public RegistrationNumber getRegistration() {
            return registration;
        }
    
        public boolean isRented() {
            return isRented;
        }
    
        //You can use this method in other methods of AbstractCar, but is implemented in your concrete classes
        public abstract Integer getCapacity();
    
        public boolean isFull() {
            if (fuelLevel == getCapacity()) {
                return true;
            } else return false;
        }
    
    
    }
    

    然后在其他功能中使用它。在你的具体类中,你定义了方法体:

    public Integer getCapacity(){
        //Your logic to calculate capacity for every concrete class here
    }
    

    【讨论】:

    • 我认为更好的方法是在AbstractCar 中设置值,因为它们都需要该值。我们会给你两个吸气剂。并且由于抽象类允许您拥有实现,因此无需为每个类覆盖 getter。
    • 如果 SmallCar 和 LargeCar 涉及不同的逻辑,您将使用覆盖。但在他的例子中,你只有一个固定值。这就是为什么我认为你的方法太过分了。
    • 如果真实案例与示例相同,是的,这种方法有点矫枉过正,但我​​认为实际用例更复杂,如果他需要逻辑来根据具体的其他数据成员计算或选择容量类,这将被使用。
    • 另外你不能强制抽象类的其他实现者具有特殊的容量值或逻辑,你不能用这种构造方法强制主题。
    • 不知道你说的强制是什么意思。我只是使用super 调用,这是编程的基础知识之一,并将值保存在抽象类中。这是一种有效且推荐的方法。您使用抽象类来隐藏继承的通用逻辑。这样您就可以避免将每个类状态保存在其对应的类中,因为该值是固定值。或者如果你不喜欢构造函数,当你想保存公共状态时,可以使用抽象类中的 getter 和 setter。
    猜你喜欢
    • 1970-01-01
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    相关资源
    最近更新 更多