2016-04-22 3 views
2

Also als Teil eines Mietwagen-Systems muss ich Klassen schreiben, um große und kleine Autos zu repräsentieren, der Unterschied zwischen diesen ist, dass sie Tanks unterschiedlicher Größe haben und Kraftstoff mit unterschiedlichen Raten verbrauchen. Mein Ansatz ist derzeit, eine Schnittstelle, Car, zu implementieren, die von einer abstrakten Klasse AbstractCar implementiert wird, die um zwei konkrete Klassen SmallCar und LargeCar ​​erweitert wird. Dies ist jedoch das erste Mal, dass ich Interfaces und abstrakte Klassen benutze (wir decken sie nur im Unterricht ab und diese Aufgabe soll unser Wissen über sie bestimmen) und ich habe Probleme zu wissen, was ich in welcher Klasse platzieren soll.Lernschnittstellen und Hierarchien, wo bestimmte Variablen und Methoden platziert werden können?

Die Fill-Methoden-Implementierungen sind genau die gleichen, sie müssen nur auf den richtigen Wert von FUEL_CAPACITY verweisen, so dass ich denke, dass ich diese Methoden in der AbstractCar-Klasse implementieren sollte, aber dann weiß ich nicht wie zu bekommen Sie beziehen sich auf die korrekten FUEL_CAPACITY-Werte. Das Feld fuelLevel wird offensichtlich auch von allen Autos gehalten, so dass ich glaube, dass ich es in AbstractCar deklarieren sollte, aber dann kann ich von den Unterklassen nicht darauf zugreifen, ohne seine Privatsphäre zu entfernen.

Würde mir jemand helfen, herauszufinden, was ich falsch mache oder was ich über Schnittstellen und Vererbung falsch verstehe? Eine Sache, die ich in Betracht gezogen habe, ist ein Enum-CarType, AbstractCar hält einen CarType als Feld und die gesamte Implementierung erfolgt in der AbstractCar-Klasse mit if-Anweisungen zum Umschalten auf den richtigen FUEL_CAPACITY-Wert und einfach mit SmallCar und LargeCar ​​als Konstruktoren oder Factory-Klassen ohne viele oder sogar tatsächliche Implementierungen.

Vielen Dank im Voraus für jede Hilfe Ich realisiere es ein bisschen langatmig, aber ich versuche sicherzustellen, dass ich die Konzepte, die wir lernen, vollständig verstehe und dass ich sie richtig implementiere anstatt nur etwas zusammen zu verpfuschen ' funktioniert, ist aber möglicherweise nicht die richtige oder eleganteste Lösung.

+0

Sie können die Logik mit den Werten zum 'AbstractCar' übertragen. Dann setze diese Werte einfach in den Controller von 'SmallCar' und' LargeCar'. Dies wäre ein Ansatz. Wie du schon gesagt hast, musst du immer die gemeinsame Logik in der Elternklasse haben. Sie möchten doppelten Code vermeiden. Dann müssen Sie nur sicherstellen, dass Sie im Konstruktor andere Werte festlegen. Und wenn Sie den fixen Wert kennen, können Sie sogar die Angabe von Parametern für 'SmallCar' oder' LargeCar' weglassen und einfach diese festen Werte im 'super()' Aufruf innerhalb des Konstruktors setzen. –

Antwort

1

Sie können die Logik mit den Werten, auf die Sie hingewiesen haben, zum AbstractCar übertragen. Legen Sie diese Werte dann einfach im Konstruktor von SmallCar und LargeCar ​​fest. Dies wäre ein Ansatz. Wie du schon gesagt hast, musst du immer die gemeinsame Logik in der Elternklasse haben. Sie möchten doppelten Code vermeiden. Dann müssen Sie nur sicherstellen, dass Sie im Konstruktor andere Werte festlegen. Und wenn Sie den Fix-Wert kennen (wie Sie es aus dem gegebenen Beispiel kennen), können Sie sogar die Angabe von Parametern für SmallCar- oder LargeCar-Konstruktoren weglassen und diese festen Werte nur im Aufruf von super() im Konstruktor festlegen.

Hier ist die Implementierung meiner Lösung.

Die Schnittstelle Car, wo ich die getFuelMethod() Methode entfernt, da die Zugriffsebene geschützt werden muss:

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 Die abstrakte Klasse:

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 

} 

Hier die ist SmallCar Implementierung:

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

Danke, ich denke, das ist die beste Antwort für meine Situation, da die Werte der Kraftstoffkapazität konstant sind, 45 für kleine und 65 für große. Wäre es besser, beide Konstanten in der Schnittstelle als SMALL_FUEL_CAPACITY und LARGE_FUEL_CAPACITY zu deklarieren oder jede in den konkreten Klassen einzeln zu deklarieren? Auch habe ich festgestellt, dass die Implementierungen von drive() unterscheiden sich geringfügig. Also muss die Laufwerksmethode in den konkreten Klassen implementiert werden, muss aber auch auf fuelLevel schreiben, was nun in der abstrakten Klasse ist. Irgendwelche Ideen, wie man dieses Problem umgehen kann? Danke nochmal! – transiti0nary

+0

Da Treibstoffkapazität eine Konstante ist, würde ich empfehlen, es in der konkreten Klasse zu deklarieren und den Wert im 'Super'-Aufruf im Konstruktor zu übergeben, wie ich vorgeschlagen habe. In Bezug auf die "Drive" -Methode haben Sie Recht. Wenn sich die Implementierung unterscheidet, Sie aber immer noch dieselbe Methodensignatur haben, empfiehlt es sich, eine abstrakte Methode in der abstrakten Klasse zu deklarieren. –

+0

Aber ich sehe jetzt, dass Sie die Methode bereits in der Schnittstelle deklariert haben, was ein noch besserer Ansatz ist. Sie sind also verpflichtet, diese Funktion in der konkreten Klasse zu implementieren - AbstractCar kann nicht instanziiert werden, dh es muss nicht die Methode 'drive' implementiert werden. –

0

Wenn Ihre Kapazität ist nur eine Eigenschaft (nur Daten) von Auto, verwenden @Jernej K approach, aber wenn die Kapazität der Berechnung kann eine gewisse Logik haben, verwenden Sie diese:

Der beste Weg ist abstrakte Methoden zu verwenden.stellen Sie eine Methode abstract Integer getCapacity(); in Ihrer abstrakten Klasse

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; 
    } 


} 

und es dann in anderen Funktionen verwenden. und in der konkreten Klasse, Sie definieren den Körper der Methode:

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

Ich denke, ein besserer Ansatz ist, den Wert in 'AbstractCar' zu setzen, weil beide diesen Wert benötigen. Wir hätten zwei Getter. Und da abstrakte Klassen Implementierungen ermöglichen, muss der Getter für jede Klasse nicht überschrieben werden. –

+0

Sie würden das Überschreiben verwenden, wenn für SmallCar und LargeCar ​​eine andere Logik erforderlich wäre. Aber mit seinem Beispiel haben Sie nur einen festen Wert. Deshalb denke ich, dass dein Ansatz über Töten ist. –

+0

Wenn der reale Fall der gleiche wie das Beispiel ist, ist dieser Ansatz zwar Overkill, aber ich denke, dass echte Anwendungsfälle komplexer sind und wenn er Logik zum Berechnen oder Auswählen von Kapazität basierend auf anderen Datenelementen der konkreten Klasse benötigt, wird diese verwendet. –

0

Wenn Sie nur FUEL_CAPACITY aus der Klasse Benutzer verbergen wollen, aber nicht von den weiteren Entwickler, können Sie erklären ihn als protected im AbstractCar und Initialisieren Sie es mit einem richtigen Wert in den untergeordneten Klassen. Auch würde ich eine Getter-MethodegetCapacity() in der AbstractCar, die diesen Wert zurückgibt.

Verwandte Themen