1. #1
    Avatar von aquastar
    Registriert seit
    08.11.2011
    Beiträge
    1.024
    Thanked 1.110 Times in 580 Posts

    Standard Abstraktion von Klassen und Methoden

    Hallo zusammen,

    hier zunächst folgender Code-Ausschnitt:


    public class RentalService {

    private List<Car> cars = new ArrayList<>();
    private List<Bike> bikes = new ArrayList<>();

    private List<Client> clients = new ArrayList<>();
    private Map<Client,List<Car>> carsPerClient = new HashMap<>();
    private Map<Client,List<Bike>> bikesPerClient = new HashMap<>();


    public int numberOfCars () {
    return cars.size();
    }

    public int numberOfBikes () {
    return bikes.size();
    }

    public boolean rentACar (Client client, Car car) {
    if (client.age() < car.getMinAge()) {
    System.out.println ("The minimum age to rent a car is "+car.getMinAge());
    return false;
    }
    if (!clients.contains (client)) {
    System.out.println ("Unknown client: "+client);
    return false;
    }
    if (!cars.contains(car)) {
    System.out.println ("Unknown car: "+car);
    return false;
    }
    List<Car> carsOfClient = carsPerClient.get (client);
    if (carsOfClient == null) {
    carsOfClient = new ArrayList<>();
    carsPerClient.put(client, carsOfClient);
    }
    carsOfClient.add (car);
    return true;
    }

    public boolean rentABike (Client client, Bike bike) {
    if (client.age() < bike.getMinAge()) {
    System.out.println ("The minimum age to rent a biker is "+bike.getMinAge());
    return false;
    }
    if (!clients.contains (client)) {
    System.out.println ("Unknown client: "+client);
    return false;
    }
    if (!bikes.contains(bike)) {
    System.out.println ("Unknown bike: "+bike);
    return false;
    }
    List<Bike> bikesOfClient = bikesPerClient.get (client);
    if (bikesOfClient == null) {
    bikesOfClient = new ArrayList<>();
    bikesPerClient.put(client, bikesOfClient);
    }
    bikesOfClient.add (bike);

    return true;
    }
    }


    Schnell zu sehen ist, dass die Funktionen rentABike und rentACar redundanten Code besitzen. Um zu abstrahieren wurde für Bike und Car eine Oberklasse "Vehicles" geschaffen, die die in den Funktionen verwendeten Eigenschaften abbildet. Um nun auch eine abstrakte Funktion zu schreiben, habe ich folgenden Ansatz:


    public boolean rentACar (Client client, Car car) {
    return rentAVehicle(client, car, this.carsPerClient, this.cars); // this nur, damit es für euch leichter ist zu erkennen, dass ich die privaten Variablen meine
    }

    public boolean rentAVehicle(Client client, Vehicle vehicle, Map<Client,List<Vehicle>> vehiclesPerClient, List<Vehicle> vehicles) {
    for (Client clientOfMap : vehiclesPerClient.keySet()) {
    if (vehiclesPerClient.get(clientOfMap).contains(vehicle)) {
    System.out.println("The vehicle " + vehicle + " is already rented by " + clientOfMap);
    return false;
    }
    }
    if (client.age() < vehicle.getMinAge()) {
    System.out.println ("The minimum age to rent a vehicle is " + vehicle.getMinAge());
    return false;
    }
    if (!clients.contains (client)) {
    System.out.println ("Unknown client: "+client);
    return false;
    }
    if (!vehicles.contains(vehicle)) {
    System.out.println ("Unknown vehicle: "+ vehicle);
    return false;
    }

    List<Vehicle> vehiclesOfClient = vehiclesPerClient.get(client);
    if (vehiclesOfClient == null) {
    vehiclesOfClient = new ArrayList<>();
    vehiclesPerClient.put(client, vehiclesOfClient);
    }
    vehiclesOfClient.add(vehicle);

    return true;
    }


    Problem ist allerdings, dass sich die List<Car> nicht zu List<Vehicle> logischerweise konvertieren lässt. Wie könnte man die o.g. Funktionen abstrahieren? Operatoren wie instanceof sollten vermieden werden, auch Typen wie z.B "? extends Vehicle".

    Über Anregungen würde ich mich sehr freuen!
    Geändert von aquastar (16.09.2017 um 02:57 Uhr)

  2. The Following 4 Users Say Thank You to aquastar For This Useful Post:

    DMW007 (16.09.2017), DotNet (19.09.2017), Negok (17.09.2017), Starbucks (30.10.2018)

  3. #2
    Avatar von Nuebel
    Registriert seit
    23.11.2013
    Beiträge
    446
    Thanked 361 Times in 236 Posts

    Standard AW: Abstraktion von Klassen und Methoden

    Die einfachste Lösung wäre, wenn Vehicle ein Interface wäre, dessen Methoden von Car und Bike implementiert werden.

    public interface Vehicle {
    public enum TYPE {
    CAR, BIKE;
    }

    public Type getType();
    public int getMinAge();
    }

    public class Car implements Vehicle {
    @Override
    public Type getType() {
    return TYPE.CAR;
    }

    @Override
    public int getMinAge() {
    return 18;
    }

    ...
    }


    public class RentalService {
    private List<Vehicle> vehicles = new ArrayList<>();
    private List<Client> clients = new ArrayList<>();

    private Map<Client, List<Vehicle>> vehiclesPerClient = new HashMap<>(); // Client muss hashCode() und equals(Object o) implementieren, um als Schlüssel für eine Map zu funktionieren

    private int numberOfCars = 0;
    private int numberOfBikes = 0;

    public int getNumberOfCars() { return numberOfCars; }
    public int getNumberOfBikes() { return numberOfBikes; }

    public boolean rentAVehicle(Client client, Vehicle vehicle) {
    if (client.getAge() < vehicle.getMinAge()) {
    System.out.println("bla");
    return false;
    }
    if (!clients.contains(client)) {
    System.out.println("bla1");
    return false;
    }
    if (!vehicles.contains(vehicle)) {
    System.out.println("bla2");
    return false;
    }
    List<Vehicle> vehiclesOfClient = vehiclesPerClient.get(client);
    if (null == vehiclesOfClient) {
    vehiclesOfClient = new ArrayList<>();
    vehiclesPerClient.put(client, vehiclesOfClient);
    }
    vehiclesOfClient.add(vehicle);
    switch (vehicle.getType()) {
    case Vehicle.TYPE.CAR: ++numberOfCars; break;
    case Vehicle.TYPE.BIKE: ++numberOfBikes; break;
    }
    return true;
    }
    }


    Aber du schreibst:
    Operatoren wie instanceof sollten vermieden werden, auch Typen wie z.B "? extends Vehicle"
    also ist die obige Lösung wohl nicht erwünscht?

    Problem ist allerdings, dass sich die List<Car> nicht zu List<Vehicle> logischerweise konvertieren lässt.
    So logischerweise ist das gar nicht. Das liegt an den Java Generics und Type Erasure nach dem Kompilieren. In Verbindung mit Generics gibt es Kovarianz, Kontravarianz und Invarianz.
    https://dzone.com/articles/covarianc...contravariance
    https://de.wikipedia.org/wiki/Kovari..._Kontravarianz

  4. The Following 4 Users Say Thank You to Nuebel For This Useful Post:

    aquastar (16.09.2017), DMW007 (16.09.2017), DotNet (16.09.2017), Starbucks (30.10.2018)

  5. #3
    Avatar von aquastar
    Registriert seit
    08.11.2011
    Beiträge
    1.024
    Thanked 1.110 Times in 580 Posts

    Standard AW: Abstraktion von Klassen und Methoden

    An ein Enum habe ich auch schon gedacht, aber wäre dies nicht fast das Gleiche als würde man instanceof verwenden? Ich würde dies als Ansatz gar nicht schlecht finden, allerdings bin ich der Meinung, dass der Prof eine funktionale Änderung an den Klassen Car etc. nicht verwünscht. Die Aufgabenstellung fordert eine Vereinfachung der RentalService-Klasse. Ich bin mir unsicher, abzuwägen was nun als wirkliche Vereinfachung gelten würde. Denn auch bei deiner Lösung müsste man im Falle eines neuen FahrzTreugs (bspw. Truck) die rentATruck-Methode und numberOfTrucks-Parameter erstellen und die Funktion rentAVehicle erweitern (beim Zählen der Autos). Durch mein Beispiel hatte ich mir erhofft, dass man für ein neues Vehicle möglichst wenig erweitern muss. Allerdings erscheint das ein wenig schwierig ohne gewisse Mittel anwenden zu dürfen.
    Interfaces dürfen leider auch nicht verwendet werden. Die Funktionen wie z.B: "rentACar" dürfen nicht gelöscht werden, da die vorherigen Signaturen zwingend bestehen bleiben müssen (Teil der Aufgabenstellung).

    Die nicht mögliche Konvertierung habe ich als "logisch" angesehen, nachdem ich mich hiermit auseinandergesetzt habe: https://stackoverflow.com/questions/...s-implicitly-p
    Logisch bleibt es dann wohl doch nicht. :-D

    Dass der Client hashCode() implementieren muss war mir bisher nicht klar, wieso das denn?

  6. #4
    Avatar von Nuebel
    Registriert seit
    23.11.2013
    Beiträge
    446
    Thanked 361 Times in 236 Posts

    Standard AW: Abstraktion von Klassen und Methoden

    Keys einer Map müssen eindeutig identifizierbar, also unterscheidbar, sein. Das stellt die (korrekte) Implementierung von hashCode() und equals(Object o) sicher. Gängige IDEs können beide Methoden automatisch erzeugen.

    Ja, bei deinem Stackoverflow Link werden, wie ich beim Überfliegen gesehen habe, die Themen, die ich auch angesprochen habe (Ko- und Kontravarianz), behandelt.

    Kannst du vielleicht mal die ganze Aufgabenstellung + sämtlichen Sourcecode posten? Es lässt sich bestimmt eine Lösung finden.

  7. The Following 4 Users Say Thank You to Nuebel For This Useful Post:

    aquastar (16.09.2017), DotNet (19.09.2017), SSIO (19.09.2017), Starbucks (30.10.2018)

Ähnliche Themen

  1. [Methoden & Hilfe] Liebe, Selbstbewusstsein & co
    Von WurstEsser im Forum Zwischenmenschliches
    Antworten: 20
    Letzter Beitrag: 12.08.2014, 08:00
  2. C# Klassen- und Instanzmethoden?
    Von Gast78236 im Forum .Net
    Antworten: 4
    Letzter Beitrag: 18.01.2014, 02:30
  3. Methoden Parameterrückgabe
    Von aquastar im Forum Java
    Antworten: 8
    Letzter Beitrag: 06.06.2012, 19:27
  4. Methoden um Browser-Accounts zu lesen?
    Von JumP-StyLe im Forum OffTopic
    Antworten: 5
    Letzter Beitrag: 03.06.2012, 19:50
  5. [MW3] Eure Klassen
    Von BMG im Forum Call of Duty
    Antworten: 4
    Letzter Beitrag: 29.05.2012, 16:56
Diese Seite nutzt Cookies, um das Nutzererlebnis zu verbessern. Klicken Sie hier, um das Cookie-Tracking zu deaktivieren.