Aufgabe 1 (Wettrennen)

Für die Simulation eines Wettrennens sollen verschiedene Fahrzeugarten objektorientiert modelliert werden. Da alle Fahrzeugtypen gemeinsame Eigenschaften haben, definieren wir uns zunächst eine Basisklasse Fahrzeug, die als Oberklasse für die anderen Klassen dienen soll. Ein Fahrzeug hat folgende allgemeinen Merkmale:

  • Seine aktuelle Position (in km und der Einfachheit halber in nur einer Dimension)
  • Seine aktuelle Geschwindigkeit (in km/h)
  • Es kann bewegt werden (Methode bewege). Die Methode wird mit einem double-Parameter aufgerufen, der die Anzahl der Minuten angibt, die sich das Fahrzeug mit der aktuellen Geschwindigkeit vorwärts bewegt. Der Methodenaufruf ändert natürlich die Position des Fahrzeugs, wenn es mit einer von 0 verschiedenen Geschwindigkeit bewegt wird.
  • Man kann seine Geschwindigkeit setzen (Methode setzeGeschwindigkeit). Die Geschwindigkeit darf die Maximalgeschwindigkeit nicht überschreiten, eine korrekte Ausführung sollte protokolliert werden.
  • Es kann seine Maximalgeschwindigkeit angeben (Methode getMaxGeschwindigkeit). Für ein Objekt der Klasse Fahrzeug soll die Maximalgeschwindigkeit 0 sein.
  • Es kann die Anzahl seiner Räder angeben. In der Klasse Fahrzeug soll diese ebenfalls 0 sein.

Nun sollen einige konkrete Fahrzeuge definiert werden, indem entsprechende Klassen von Fahrzeug abgeleitet werden:

  • Ein Fahrrad ist ein Fahrzeug mit 2 Rädern und Maximalgeschwindigkeit 30 km/h.
  • Ein Auto ist ein Fahrzeug mit 4 Rädern und Maximalgeschwindigkeit 140 km/h.
  • Ein Rennwagen ist ein Auto mit Maximalgeschwindigkeit 220 km/h.
  • Ein Krankenwagen ist ein Auto mit einem zusätzlichen Blaulicht, das ein- oder ausgeschaltet sein kann (neues Attribut!). Außerdem muss der Krankenwagen Methoden zum Ein- bzw. Ausschalten des Blaulichts anbieten.

Definieren Sie diese Klassen und nutzen Sie dabei so weit wie möglich die Vererbung von Eigenschaften aus!

Aufgabe 2 (… das eigentliche Wettrennen zu Aufgabe 1)

Nun soll die eigentliche Simulation des Wettrennens in einer Klasse Wettrennen geschrieben werden. Erzeugen sie je ein Fahrzeug und setzen Sie dann die Geschwindigkeiten auf:

  • Fahrrad 20 km/h
  • Auto 150 km/h
  • Rennwagen 200 km/h
  • Krankenwagen 80 km/h

Dann sollen sich die Fahrzeuge bewegen. Der Gerechtigkeit halber geben wir dem Fahrrad einen Vorsprung von 4 Stunden. Danach lassen Sie alle Fahrzeuge eine Stunde lang mit unveränderter Geschwindigkeit vorwärts fahren. Was fällt Ihnen beim Auto auf?

Aufgabe 3

Eine Firma besitzt Geschäftskunden, von denen sie die Telefonnummer, eine E-Mail-Adresse und eine Adresse verwaltet. Ein Geschäftskunde kann ein Lieferant oder ein Kunde sein. Von den Lieferanten sollte der Firmenname und ein Ansprechpartner gespeichert werden. Die Kunden werden durch eine Kundennummer, einen Namen und die Anzahl ihrer Bestellungen erfasst.

Modellieren Sie geeignete Klassen Geschäftskunde, Lieferant und Kunde im UML-Klassendiagramm.

Aufgabe 4 (Spielzeuge)

Eine Spielzeugfirma möchte am derzeitigen Star-Wars-Boom teilhaben und erweitert ihre Angebotspalette.

Sie möchte vorerst verschiedene Raumfahrzeuge und Spielfiguren als Spielzeuge anbieten. Alle Spielzeuge bestehen aus „weichem Material“ und sind wie beispielsweise der Todesstern als Anti-Stress-Ball zu verwenden. Anträge der Projektgruppe, Joda einen rosa Mantel zu geben oder zumindest die Gruppe der Jediritter mit einem Einhorn zu verstärken, wurden von der Walt Disney Company abgelehnt. Als Raumfahrzeuge werden Todessterne, X-Wings und Imperiale Zerstörer angeboten. Zu den Todessternen und Imperialen Zerstörern gehören Raumfähren, welche selbst ebenfalls Raumfahrzeuge sind. Als Spielfiguren werden Jedi-Ritter und Stormtrooper angeboten. Jedi-Ritter erhalten ein angepasstes Laserschwert, die Stormtrooper austauschbare Helme und Karabiner (Gewehre).

  1. Entwerfen Sie für das beschriebene System ein UML-Beziehungsdiagramm (ohne Attribute und Methoden).
  2. Erläutern Sie mit Hilfe des gegebenen Systems folgende Begriffe und Zusammenhänge:     
    Vererbung, Generalisierung und  Spezialisierung, abstrakte Klasse
  3. Erläutern Sie, welche Auswirkungen eine Beziehung der Art Assoziation, Aggregation und Komposition auf einen Jedi-Ritter und sein Laserschwert hat. Begründen Sie hier Ihre Wahl für Teilaufgabe a).

Aufgabe 5 (Pyramiden)

Quadratische Pyramide, Tetraeder und Kegel besitzen eine Grundfläche, die durch die Angabe einer einzigen Länge bestimmt ist, und eine Höhe; aus diesen Größen lassen sich Volumen und Oberfläche berechnen.

Implementieren Sie eine geeignete Klasse Pyramide und leiten Sie Klassen Tetraeder und Kegel ab. Implementieren Sie ein geeignetes Rahmenprogramm zum Testen der Klassen.

Aufgabe 6 (Konten)

Die bekannte Bank .comumweg bietet verschiedene Konten an. Für alle Konten sollte natürlich ein Kunde (Name, Adresse, Telefon) erfasst werden, jeder Kunde kann 10 Konten haben. Jedes Konto hat eine Kontonummer, einen Kontostand, Einzahlungen und Auszahlungen. Es werden (u. a.) verschiedene Arten an Konten angeboten:

  • Girokonto (Kreditrahmen, Karte, Bonuszahlungen)
  • Anlagekonten
    • Tagesgeldkonto (Zinsen)
    • Festgeldkonto (Zinsen, Laufzeit)

Implementieren Sie ein Modell für diesen Sachverhalt als UML-Diagramm und testen Sie dieses im Java-Editor an geeigneten Daten.

Hinweis: Ein Objekt Kreditkarte soll hier nicht modelliert werden, es genügt eine Nummer.

Aufgabe 7 (Flugzeuge)

Im Rahmen einer Softwareentwicklung für die einheitliche Verwaltung von Flugzeugen wurde die Klasse Flugzeug entwickelt. Die Klasse Flugzeug ist auszugsweise angegeben:

public abstract class Flugzeug {
  private String hersteller;            // Herstellername
  private int maxSpeed;                 // Max. Geschwindigkeit
  private String immatNummer;           // Immatrikulationsnummer     
  private int anzahlFluegel = 1;        // Anzahl Flügelpaare

  public Flugzeug(String hersteller, int maxSpeed, int anzahlFluegel) {       
    this.hersteller = hersteller;
    this.maxSpeed = maxSpeed;
    this.anzahlFluegel = anzahlFluegel;
  }

  public String getImmatNummer() {
    return immatNummer;
  }

  protected void setImmatNummer(String immatNummer) {
    this.immatNummer = immatNummer;
  }

  public int getMaxSpeed() {
    return maxSpeed; 
  }

  abstract public boolean getLooping();

  // weitere Methoden
}

Ausgehend von dieser Klasse sind zwei Klassen Verkehrsflugzeug und Doppeldecker zu modellieren. Diese sollen folgende Spezifikationen erfüllen:

Klasse Verkehrsflugzeug:

  • Ein Verkehrsflugzeug ist ein Flugzeug, das genau ein Flügelpaar sowie eine zusätzliche Variable für die Anzahl der Passagiere hat.
  • Ein Verkehrsflugzeug fliegt keine Loopings. Stellen Sie deshalb sicher, dass die Methode getLooping immer false zurückgibt, auch in allen Unterklassen von Verkehrsflugzeug.
  • Ein Verkehrsflugzeug-Objekt kann mit Angabe des Herstellers (String), der maximalen Geschwindigkeit (int), der Immatrikulationsnummer (String) und der Anzahl der Passagiere (int) erzeugt werden.
  • Die Klasse Verkehrsflugzeug hat die Methoden getAnzahlPassagiere und setAnzahlPassagiere zum Abfragen und Setzen der Anzahl der Passagiere.

Klasse Doppeldecker:

  • Ein Doppeldecker ist ein Flugzeug, das genau zwei Flügelpaare hat.
  • Weiter ist ein Doppeldecker akrobatiktauglich, d.h. man kann damit Loopings fliegen. Für einen Looping muss der Doppeldecker eine Mindestgeschwindigkeit von 320 km/h erreichen. Definieren Sie dafür eine Konstante LOOPINGSPEED. Die Methode getLooping soll true zurückgeben, falls die zulässige max. Geschwindigkeit (maxSpeed) grösser LOOPINGSPEED ist.
  • Die Klasse Doppeldecker hat eine Variable offenesCockpit vom Typ boolean. Sie gibt an, ob der Doppeldecker ein offenes oder geschlossenes Cockpit hat. Nachdem offenesCockpit gesetzt worden ist, darf sie nicht mehr verändert werden. Der Wert des Attributes darf gelesen werden.
  • Ein Doppeldecker-Objekt kann durch 2 Konstruktoren initianlisert werden:
    • Der 1. Konstruktor hat folgende Parameter: Hersteller (String), maximale Geschwindigkeit (int), die Immatrikulationsnummer (String) und einen boolean offenesCockpit, der angibt, ob der Doppeldecker ein offenes oder geschlossenes Cockpit hat.
    • Der 2. Konstruktor hat folgende Parameter: Hersteller (String), maximale Geschwindigkeit (int), die Immatrikulationsnummer (int). Der Defaultwert für offenesCockpit bei einem Doppeldecker ist true.
  • Die Klasse Doppeldecker soll nicht erweiterbar sein.
  1. Modellieren Sie die beschriebenen Klassen in einem UML-Klassendiagramm.
  2. Implementieren Sie die Klassen Verkehrsflugzeug und Doppeldecker.

Mögliche Lösung

Aufgabe 8 (Textdatei; lange Zeilen)

Implementieren Sie ein Konsolenprogramm LangeZeilen, das auf der Kommandozeile zwei Parameter erwartet: erstens eine ganze Zahl n und zweitens den Namen (inkl. evtl. Pfad) einer Datei (Textdatei). Das Programm soll die Datei zeilenweise einlesen und zählen, wie viele Zeilen länger als n Zeichen sind.

Das Ergebnis soll in einen Satz auf die System-Konsole ausgeben werden: „Die Datei <NameDerDatei> enthält <Anzahl> Zeilen, die länger als <n> Zeichen sind.“ (Dabei sollen für <NameDerDatei>, <Anzahl> und <n> die aktuellen Werte eingefügt werden.)

Falls ein Benutzer das Programm aufruft mit weniger als zwei Parametern, soll das Programm eine Kurzanleitung auf die System-Konsole schreiben („Aufruf: …“) mit der Angabe, wie der korrekte Aufruf aussieht und was die Parameter bedeuten.

Hinweis: Gesamter Code darf in der main-Methode stehen.

Aufgabe 9 (Tickets; Polymorphismus)

Gegenstand der Aufgabe ist eine Ticketkontrolle. Konzentrieren Sie sich auf die Aufgabenstellung und nicht auf irgendwelche Eigenheiten in Ihrem Lieblingsverkehrsverbund. So können Sie davon ausgehen, dass Fahrgäste immer kontrolliert werden (keine Selbstkontrolle). Einzeltickets und Mehrfahrtenkarten sind immer nur in einer Zone gültig. Generalabos gelten im gesamten Netz.

public abstract class Ticket {
  private int preis;  // Der Preis
  private boolean entwertet;  // Ungültig, wenn entwertet!

  public Ticket( int preis ) {
    this.preis = preis;
    entwertet = false;
  }

  public int getPreis() { return preis; }
  public void entwerten() { entwertet = true; }
  public boolean istEntwertet() { return entwertet; }
  public abstract boolean gueltigInZone( int zone ); }
}

//--------------------------------------------------
public class Einzelticket extends Ticket {
  private int zone;  // Nur in dieser Zone gültig!
  private String verfallsDatum;  // String im Format JJJJMMTT
                                 // Nach diesem Datum ungültig!

  public Einzelticket(int preis, String verfallsDatum, int zone) {
    super(preis);
    this.verfallsDatum = verfallsDatum;
    this.zone = zone; 
  }

  public String getVerfallsDatum() { return verfallsDatum; }
  public boolean gueltigInZone(int zone) { return zone == this.zone; }
}

//--------------------------------------------------
public class Mehrfahrtenkarte extends Ticket {
  private int fahrten;  // Anzahl erlaubte Fahrten
  private int zone;  // Nur in dieser Zone gültig!

  public Mehrfahrtenkarte(int preis, int fahrten, int zone) {
    super(preis);
    this.fahrten = fahrten;
    this.zone = zone;
  }

  public void entwerten() {
    if( fahrten == 0 ) return;
    fahrten -= 1;
  }

  public boolean istEntwertet() { return fahrten == 0; }
  public boolean gueltigInZone( int zone ) { return zone == this.zone; }
}

//--------------------------------------------------
public class Generalabo extends Ticket {
  private String inhaber;  // Nur für diese Person gültig!
  private String verfallsDatum;  // String im Format JJJMMTT
                                 // Nach diesem Datum ungültig!

  public Generalabo(int preis, String inhaber, String verfallsDatum) {
    super(preis);
    this.inhaber = inhaber;
    this.verfallsDatum = verfallsDatum; 
  }

  public void entwerten() { /* es passiert nix! */ }
  public String getVerfallsDatum() { return verfallsDatum; }
  public boolean gueltigInZone( int zone ) { return true; }
  public String gibInhaber() { return inhaber; }
}

Vervollständigen Sie nun folgendes Gerüst einer Ticketkontrolle. Der Kontrolleur hat zur Verfügung:

  1. Die Namen aller Fahrgäste.
  2. Ihre Tickets. Dabei gilt: tickets[i] ist das Ticket des Fahrgasts mit Namen namen[i].
  3. Die aktuelle Zone, in der die Kontrolle stattfindet.
  4. Das heutige Datum.

Der Kontrolleur muss alle Tickets entwerten, und die Anzahl der Schwarzfahrer zählen. Ein entwertetes Ticket ist nie gültig. Ansonsten ist die Gültigkeit vom Typ des Tickets abhängig. Überlegen Sie sich sorgfältig das richtige Kriterium.

Hinweis: Das Datum ist als String abgelegt in der Form „JJJJMMTT“, d.h. Weihnachten 2020 ist „20201224“. Solche Datentypen kann man lexikographisch miteinander vergleichen (d.h. gemäß Ihrer „alphabetischen“ Reihenfolge. Sie können die String-Methode compareTo verwenden).

Das Programmgerüst:

public int kontrolliere( String[] namen, Ticket[] tickets, int zone, String datum ) {
  int schwarzFahrer = 0;

  if( namen.length != tickets.length )
    throw new IllegalArgumentException(); }

  // IHR CODE HIER!

  return schwarzFahrer;
}

Aufgabe 10 (AttentionScrollbar)

In dieser Aufgabe entwickeln Sie eine Klasse AttentionScrollbar, die von der Klasse Scrollbar des Packages java.awt abgeleitet ist. Die Klasse AttentionScrollbar unterscheidet sich von der Klasse Scrollbar durch folgende zusätzliche(!) Eigenschaften:

  • Wenn der eingestellte Wert des AttentionScrollbars durch den Schieberegler mehr als die Hälfte des einstellbaren Bereiches (Mittelwert) erreicht hat, soll die Hintergrundfarbe rot werden. Fällt der Wert des eingestellten Bereiches wieder unter diesen Mittelwert, so soll die Hintergrundfarbe wieder die ursprüngliche Hintergrundfarbe annehmen.
  • Eine neue Methode public int getCritical() liefert die Anzahl, wie häufig der Mittelwert durch Änderung des Schiebereglers überschritten wurde und somit die Hintergrundfarbe auf rot eingestellt wurde. Am Anfang ist dieser Wert auf 0.

Damit Sie die Klasse AttentionScrollbar ohne Nachschlagen der Klasse Scrollbar lösen können, sind hier die zur Lösung der Aufgabenstellung benötigten Methoden aufgelistet und kurz erläutert:

  1. public void setBackground(Color c) … setzt Hintergrundfarbe
  2. public Color getBackground() … liest momentane Hintergrundfarbe
  3. public void setValue(int newValue) … setzt den Wert des Scrollbars. Diese Methode wird automatisch aufgerufen, wenn der GUI Benutzer den neuen Wert mit Hilfe des Schiebers neu einstellt. Diese Methode muss daher in der Klasse AttentionScrollbar überschrieben werden.
  4. public Scrollbar(int orientation, int value, int visible, int minimum, int maximum) … Konstruktor der Klasse Scrollbar
  5. public int getMinimum() und public int getMaximum() … liefern den kleinsten bzw. größten einstellbaren Wert des Scrollbars

Implementieren Sie die Klasse AttentionScrollbar vollständig und testen Sie diese in einem geeigneten GUI-Programm. Die Anzahl der Mittelwertsüberschreitungen könnte einfach per Button oder direkt über ein Event des AdjustmentListener ausgelesen und (z. B. in einem Label) angegeben werden.

Mögliche Lösung

Aufgabe 11 (Ausweis, Polymorphismus)

Gegeben ist eine Klassenhierarchie mit den Klassen Ausweis, Identitaetskarte, Pass und Fahrausweis.

public class Ausweis {
  protected int nummer;           // Nummer des Ausweises
  protected String inhaber;       // Inhaber des Ausweises
  protected String ablaufdatum;   // Ablaufdatum des Ausweises

  public Ausweis(int nummer, String inhaber, String ablaufdatum) {
    this.nummer = nummer;
    this.inhaber = inhaber;
    this.ablaufdatum = ablaufdatum;
  }

  public int getNummer() { return nummer; }
  public String getInhaber() { return inhaber; }
  public String getAblaufdatum() { return ablaufdatum; }
}

//--------------------------------------------------
public class Identitaetskarte extends Ausweis {
  public int groesse;         // Groesse des Inhabers
  public String buergerort;   // Buergerort des Inhabers

  public Identitaetskarte(int nummer, String inhaber,
                          String ablaufdatum, int groesse,
                          String buergerort) {
    super(nummer, inhaber, ablaufdatum);
    this.groesse = groesse;
    this.buergerort = buergerort;
  }

  public int getGroesse() { return groesse; }
  public String getBuergerort() { return buergerort; }
}

//--------------------------------------------------
public class Pass extends Identitaetskarte {
  public String fingerabdruck;   // Digitaler Fingerabdruck
                                 // des Inhabers

  public Pass(int nummer, String inhaber, String ablaufdatum,
              int groesse, String buergerort, String fingerabdruck) {
    super(nummer, inhaber, ablaufdatum, groesse, buergerort);
    this.fingerabdruck = fingerabdruck;
  }

  public String getFingerabdruck() { return fingerabdruck; }
}

//--------------------------------------------------
public class Fahrausweis extends Ausweis {
  public String kategorien;   // Fahrzeugkatogerien, die der Inhaber
                              // lenken darf

  public Fahrausweis(int nummer, String inhaber, String ablaufdatum,
                     String kategorien) {
    super(nummer, inhaber, ablaufdatum);
    this.kategorien = kategorien;
  }

  public String getKategorien() { return kategorien; }
}

Eine weitere Klasse AusweisVerwaltung implementiert eine einfache Konsolen-Applikation, welche die Ausweise verwaltet. Ihre Aufgabe ist es, Teile dieser Applikation zu implementieren. Verwenden Sie dazu das nachfolgende Programmiergerüst:

public class AusweisVerwaltung {
  private Ausweis[] ausweise;
 
  public AusweisVerwaltung() {
    ausweise = new Ausweis[100];

    // Erzeugen diverser Ausweise (es ist nur eine Auswahl angegeben)
    ausweise[0] = new Identitaetskarte(1234567, "Max Meier",
                                       "10.05.2010", 178, "Chur"); 
    ausweise[7] = new Pass(76596678, "Eva Mueller", "31.12.2008",
                           169, "Zuerich", "10010110");
    ausweise[16] = new Fahrausweis(5126578, "Ida Fuchs",
                                   "20.03.2015", "A B D1"); 

    // Verwendung der von Ihnen zu implementierenden Methoden 
    System.out.println("Total Ausweise: " + countAusweise(ausweise));
    int[] countResult = countAusweisTyp(ausweise);
    System.out.println("Identitaetskarten: " + countResult[0] + 
                       ", Paesse: " + countResult[1] +
                       ", Fahrausweise: " + countResult[2]);
    printPaesse(ausweise);
  }

  public static void main(String args[]) { 
    AusweisVerwaltung av = new AusweisVerwaltung();
  }

  // Zählt die Anzahl Ausweise im Array ausweise
  public int countAusweise(Ausweis[] ausweise) {
    // Ihre Aufgabe 1)
  }

  // Zählt die Anzahl Ausweise im Array ausweise nach Ausweis-Typ
  public int[] countAusweisTyp(Ausweis[] ausweise) {
    // Ihre Aufgabe 2)
  }

  // Gibt die Paesse im Array ausweise auf der Konsole aus
  public void printPaesse(Ausweis[] ausweise) {
    // Ihre Aufgabe 3)
  }
}
  1. Implementieren Sie die Methode countAusweise vollständig, welche die Anzahl Ausweise im Parameter ausweise zurückgibt.
  2. Implementieren Sie die Methode countAusweisTyp vollständig, welche die Anzahl Identitätskarten, Pässe und Fahrausweise im Parameter ausweise in dieser Reihenfolge in einem int-Array der Länge 3 zurückgibt.
  3. Implementieren Sie die Methode printPaesse vollständig, welche die Pässe im Parameter ausweise zeilenweise auf der Konsole ausgibt. Eine Zeile soll alle Attribute eines Passes enthalten, jeweils mittels einem Komma getrennt.