2016-04-22 5 views
2

Так как часть системы проката автомобилей мне нужно написать классы для представления больших и маленьких автомобилей, разница между ними заключается в том, что они имеют разные емкости и потребляют топливо с разной скоростью. В настоящее время мой подход состоит в том, чтобы иметь интерфейс Car, реализованный абстрактным классом AbstractCar, который расширяется двумя конкретными классами SmallCar и LargeCar. Однако это мой первый раз, когда мы используем интерфейсы и абстрактные классы (мы просто освещаем их в классе, и это назначение предназначено для оценки наших знаний о них), и у меня возникают проблемы с пониманием того, что делать в каком классе.Изучение интерфейсов и иерархий, где размещать определенные переменные и методы?

Реализации метода заполнения точно такие же, им просто нужно обратиться к правильному значению FUEL_CAPACITY, поэтому он чувствует, что должен реализовывать эти методы в классе AbstractCar, но тогда я не знаю, как получить чтобы ссылаться на правильные значения FUEL_CAPACITY. Поле FuelLevel также очевидно удерживается всеми автомобилями, поэтому кажется, что я должен объявить его в AbstractCar, но тогда я не могу получить к нему доступ из подклассов, не удаляя его конфиденциальность.

Может ли кто-нибудь помочь мне выяснить, что я делаю неправильно или непонимаю о интерфейсах и наследовании? Одна вещь, которую я рассматривал, заключается в создании enum CarType, когда AbstractCar содержит CarType в качестве поля, и вся реализация выполняется в классе AbstractCar, используя операторы if для переключения на правильное значение FUEL_CAPACITY и просто используя SmallCar и LargeCar ​​в качестве конструкторов или фабричные классы без особых или даже реальных реализаций.

Заранее благодарим за любую помощь. Я понимаю, что он немного длинный, но я стараюсь, чтобы я полностью понимал концепции, которые мы изучаем, и что я их правильно реализую, а не просто собираю то, работает ", но может и не обязательно быть правильным или самым изящным решением.

+0

Вы можете передать логику в 'AbstractCar' со значениями. Затем просто установите эти значения в contrustor 'SmallCar' и' LargeCar'. Это был бы один подход. Как вы указали, вы всегда должны иметь общую логику в родительском классе. Вы хотите избежать дублирования кода. Тогда вам просто нужно убедиться, что вы устанавливаете разные значения в конструкторе. И если вы знаете значение fix, вы можете даже опустить параметры «SmallCar» или «LargeCar» и просто установить эти фиксированные значения в вызове 'super()' внутри конструктора. –

ответ

1

Вы можете передать логику в AbstractCar со значениями, как вы указали. Затем просто установите эти значения в конструкторе SmallCar и LargeCar. Это был бы один подход. Как вы указали, вы всегда должны иметь общую логику в родительском классе. Вы хотите избежать дублирования кода. Тогда вам просто нужно убедиться, что вы устанавливаете разные значения в конструкторе. И если вы знаете значение fix (как и в данном примере), вы можете даже опустить параметры для SmallCar или конструкторов LargeCar ​​и просто установить эти фиксированные значения в вызове super() внутри конструктора.

Вот реализация моего решения.

Интерфейс Car, где я REMOVED метод 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 

} 
+0

Спасибо, я думаю, что это лучший ответ для моей ситуации, поскольку значения топливной емкости постоянны, 45 для малых и 65 для больших. Было бы лучше, если бы объявить обе эти константы в интерфейсе SMALL_FUEL_CAPACITY и LARGE_FUEL_CAPACITY или объявить каждую отдельно в конкретных классах? Кроме того, я понял, что реализации drive() немного отличаются. Таким образом, метод привода должен быть реализован в конкретных классах, но также необходимо написать в FuelLevel, который теперь находится в абстрактном классе. Есть идеи, как обойти эту проблему? Еще раз спасибо! – transiti0nary

+0

Поскольку топливная емкость является постоянной, поэтому я бы рекомендовал объявить ее в конкретном классе и передать значение в вызове 'super' в конструкторе, как я и предложил. Что касается метода 'drive', вы правы. Если реализация отличается, но у вас все еще есть одна и та же сигнатура метода, тогда рекомендуется объявить абстрактный метод в абстрактном классе. –

+0

Но теперь я вижу, что вы уже объявили метод в интерфейсе, что является еще лучшим подходом. Таким образом, вы НЕОБХОДИМО реализовать эту функцию в конкретном классе - AbstractCar не может быть создан, что означает, что ему не нужно внедрять метод 'drive'. –

0

Если емкость только одно свойство (только данные) из автомобиля, используйте @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 
} 
+0

Я думаю, что лучший подход состоит в том, чтобы установить значение в 'AbstractCar', потому что они оба нуждаются в этом значении. У нас есть два геттера. И так как абстрактные классы позволяют реализовать реализации, нет необходимости переопределять getter для каждого класса. –

+0

Вы бы использовали переопределение, если бы была небольшая логика для SmallCar и LargeCar. Но с его примером у вас есть только одно значение fix. Вот почему я думаю, что ваш подход более убит. –

+0

Если реальный случай такой же, как в примере, да, этот подход является излишним, но я считаю, что реальные варианты использования более сложны, и если ему нужна логика для вычисления или выбора емкости на основе других членов данных конкретного класса, это будет использоваться. –

0

Если вы просто хотите, чтобы скрыть FUEL_CAPACITY от пользователя класса, но не от дальнейших разработчиков, вы можете объявить его как protected в AbstractCar и initiallize его с правильным значением в дочерних классах. Также я объявляю геттерный методgetCapacity() в AbstractCar, который возвращает это значение.