Assoziationen

Aus ComeniusWiki
Version vom 24. April 2016, 13:55 Uhr von B.Schiller (Diskussion | Beiträge)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Am Ende des Kapitels solltest du

  • die Begriffe Assoziation und Multiplizität kennen,
  • die Funktion einer Referenzvariable nachvollziehen können,
  • mit dem Datentyp Arraylist und den Methoden dieser Klasse umgehen können,
  • Klassenvariablen und Klassenmethoden kennen und anwenden können.

Eine wichtige Eigenschaft objektorientierter Systeme ist der Austausch von Botschaften zwischen Objekten. Dazu muss ein Objekt andere Objekte "kennen" und kann Botschaften weitergeben. Diese Verbindung nennt sich Assoziation. Bei Assoziationen ist zu unterscheiden, ob nur eine Seite die andere kennt oder ob eine Navigation in beiden Richtungen möglich ist:

  • Eine unidirektionale Beziehung geht nur in eine Richtung.
  • Eine bidirektionale Beziehung geht in beide Richtungen.

Daneben ist bei Beziehungen die Multiplizität zu beachten. Sie sagt aus, mit wie vielen Objekten eine Seite eine Beziehung haben kann. Übliche Beziehungen sind 1:1 und 1:n. Wir beschränken uns hier auf unidirektionale Beziehungen.


Implementierung einer unidirektionalen 1:1-Beziehung

Bei bekannten Computerspielen kann ein Spieler seine Fahrkünste auf einem Formel 1-Kurs testen oder als Pilot einen Flughafen anfliegen. Dabei muss jeweils ein Spieler mit einem Kurs/Flughafen assoziiert werden. Damit ein Spieler sich auf einem Kurs befinden kann, lässt sich in Spieler eine Referenzvariable vom Typ Kurs anlegen. In Java sähe das in etwa so aus:

public class Spieler {
 
    public Kurs k;
 
}
 
 
public class Kurs {
 
 
}

Während des Spiels müssen dann natürlich noch entsprechende Referenzen gesetzt werden:

public class Spiel {
 
 
Spieler uschi = new Spieler();
Kurs   hockenheim  = new Kurs();
uschi.k   = hockenheim;  
 
}


Aufgabe 1

Erstelle eine Klasse Punkt, die die Attribute x und y und entsprechende Getter- und Setter-Methoden hat, und eine Klasse Kreis mit den Attributen radius und Mittelpunkt. Die Klasse Kreis soll zwei Methoden zum Verändern des Mittelpunktes und zum Verändern des Radius erhalten.


Aufgabe 2

Für die Verwaltung einer Spedition werden zwei Klassen angelegt: Eine Klasse LKW, in dessen Konstruktor Marke, Tankinhalt und Verbrauch (in l pro 100 km) initialisiert werden. Die Klasse bekommt Getter-Methoden für Tankinhalt und Verbrauch und eine Setter-Methode für den Tankinhalt. Die Klasse Fahrer initialisiert im Konstruktor Name, Vorname und LKW und die Fahrtstrecke wird auf 0 gesetzt. Weiterhin bekommt die Klasse eine Methode fahren(int strecke), die die gefahrene Strecke speichert und den Tankinhalt aktualisiert. Eine weitere Methode benzinstand() gibt den aktuellen Tankinhalt zurück.

Hinweise: Zuerst ein LKW-Objekt erzeugen und dieses beim Konstruktor des Fahrers durch einen Mausklick als Parameterwert übernehmen. Der Benzinverbrauch auf einer bestimmten Strecke errechnet sich aus (Strecke*Verbrauch in l) / 100.




Implementierung einer unidirektionalen 1:n-Beziehung

Immer dann, wenn ein Objekt auf mehrere andere Objekte verweisen muss, reicht eine einfache Referenzvariable vom Typ der anderen Seite nicht mehr aus. Dann werden Datenstrukturen benötigt, die mehrere Referenzen aufnehmen können. In Computerspielen kann ein Spieler häufig Gegenstände, Waffen etc. sammeln. Um dies in der Programmierung umzusetzen, müssen auf der 1-Seite eine Datenstruktur verwenden, die eine Anzahl anderer Objekte aufnimmt. Handelt es sich um eine feste Anzahl von Gegenständen, ist als Datenstruktur das bereits bekannte Array gut geeignet. Bei Spielen, in denen sich die Anzahl der Gegenstände dynamisch ändert, ist ein Array wenig elegant, da die manuelle Vergrößerung oder Verkleinerung des Arrays aufwendig ist. Hier kann man beispielsweise die Klasse ArrayList aus dem Package java.util.ArrayList zum Einsatz bringen.


Exkurs:Die Klasse ArrayList

Die Klasse ArrayList dient zum Speichern von Objekten. Die ArrayList, hat gegenüber dem Array den Vorteil, dass sie jederzeit vergrößert oder verkleinert werden kann. Darüber hinaus benötigt die ArrayList als normales Java-Objekt keine besondere Array-Syntax. Allerding kann die ArrayList nur Elemente vom Typ Object speichern, d.h. es ist (zumindest ohne weitere Vorarbeit) ungeeignet um Zahlen abzuspeichern. Für die Zwecke der objektorientierten Programmierung ist sie allerdings ein sehr geeignetes Werkzeug und bietet einige sehr hilfreiche Methoden (siehe Dokumentation). Die wichtigsten sind hier in einem Anwendungsbeispiel demonstriert:


import java.util.ArrayList;
 
public class ArrayListBeispiel {
 
   public ArrayListBeispiel() {
 
        //anlegen einer ArrayListe mit Objekten vom Typ String
        ArrayList<String> liste = new ArrayList<String>();
 
        //fuellen der Liste mit Daten
        liste.add("Keule");
        liste.add("Morgenstern");
        liste.add("Wattebausch");
        liste.add("Schleuder");
        liste.add("Armbrust");
 
        //Ausgeben der Liste
        System.out.println("Alle meine Waffen:");
        System.out.println(liste); 
        System.out.println(); 
 
        //Mit dem Wattebausch kann man keinen schrecken!
        liste.remove("Wattebausch");
 
        // nochmal ausgeben
        System.out.println("Liste nach Entfernen des Wattebauschs");
        System.out.println(liste);
        System.out.println();
 
 
        // Testen ob der Wattebausch noch enthalten ist:
        if (liste.contains ("Wattebausch")){
            System.out.println("Der Wattebausch ist noch drin.");
        }else{
            System.out.println("Der Wattebausch ist draußen.");
        }
 
        // Testen ob Morgenstern enthalten:
        if (liste.contains ("Morgenstern")){
            System.out.println("Der Morgenstern ist noch drin.");
        }else{
            System.out.println("Der Morgenstern ist draußen.");
        }
 
 
        // Das erste Element der Liste abfragen
        System.out.println();
        System.out.print("Das erste Element der Liste:  ");
        System.out.println(liste.get(0));
 
        // Die Größe der ArrayList abfragen
        System.out.println(); 
        System.out.print("Laenge der ArrayList:  ");
        System.out.println(liste.size());
        System.out.println();
 
        //Auflisten aller Elemente über erweiterte Schleife
         for (String s : liste) {         //gelesen: für alle s vom Typ String in der ArrayList liste
             System.out.println(s);
        }
 
    }
}

Ausgabe:

Alle meine Waffen:
[Keule, Morgenstern, Wattebausch, Schleuder, Armbrust]
 
Liste nach Entfernen des Wattebauschs
[Keule, Morgenstern, Schleuder, Armbrust]
 
Der Wattebausch ist draußen.
Der Morgenstern ist noch drin.
 
Das erste Element der Liste:  Keule
 
Laenge der ArrayList:  4
 
Keule
Morgenstern
Schleuder
Armbrust


Aufgabe 1

Das bereits bekannte Bank soll nun erweitert werden. Zuerst erhält die Klasse Konto noch zwei Methoden einzahlen() und auszahlen(), mit denen ein bestimmter Betrag ein- bzw. ausgezahlt werden kann. Dann erstellen wir eine Klasse Bank, die eine ArrayList vom Typ Konto enthält. Dazu noch eine Methode zum Hinzufügen von Konten, eine Methode zur Ausgabe sämtlicher Kunden mit den aktuellen Kontoständen und einer Methode, mit der ein Betrag von einem Konto auf ein anderes überwiesen werden kann.

Um die Funktionalität zu überprüfen, erstellt man in BlueJ zuerst einmal drei Konto-Objekte und anschließend ein Bank-Objekt. Ruft man im Bank-Objekt die Methode hinzufuegen(Konto k) auf, dann kann man durch Klicken auf ein Konto-Objekt dieses in die ArrayList übernehmen. Auf diese Weise kann man auch bei der Methode ueberweisen(Konto a, Konto b, int betrag) die Parameterwerte für die Konten übergeben. Anschließend kann man verschiedene Kontobewegungen simulieren und entweder über den Menüeintrag Inspect bei den Konto-Objekten oder über den Aufruf der Daten mit der Ausgabemethode überprüfen.


Aufgabe 2

Ausgangspunkt sei die ebenfalls bekannte Klasse Schueler. Erstelle eine Klasse Schule, die eine ArrayList zum Abspeichern der Schüler enthält. Darüberhinaus enthält die Klasse eine Methode schuelerHinzufuegen(), mit der neue Schüler mit ihren Daten in die ArrayList eingefügt werden können, eine Methode schuelerzahlAusgeben(), die die Anzahl der Schüler in der Liste ausgibt und eine Methode aktuelleListe(), die die in der ArrayList enthaltenen Schueler mit ihren Daten ausgibt.


Aufgabe 3

Der Handyverkäufer Checker D. baut für sein Geschäft eine Kundendatei auf. In einer Klasse Kunde können Kundenobjekte mit den Attributen Name, Vorname, Wohnort und Anzahl (der Handys) erzeugt werden. Es sind für die Attribute nur die entsprechenden Getter-Methoden zu erstellen. Eine weitere Klasse Kundenverwaltung enthält eine ArrayList zur Verwaltung der Kunden. Daneben enthält die Klasse mehrere Methoden. Mit der Methode kundeHinzufuegen() können neue Kunden in die ArrayList eingefügt werden (Es darf angenommen werden, dass die Kundenobjekte bereits erzeugt wurden.). Eine Methode kundenliste() gibt sämtliche Kundendaten in einer Liste aus. Mit wohnortSuche kann nach Eingabe eines Ortes ermittelt werden, wieviele Kunden aus dem entsprechenden Wohnort stammen (Hinweis: Hier ist ein Stringvergleich notwendig). Die Methode gesamtzahl() schließlich stellt fest, wieviele Kundenhandys das Geschäft insgesamt betreut.



Wenn wir nun von einem Spiel ausgehen, in dem ein Spieler Waffen sammeln kann, könnte die Umsetzung mit Hilfe einer ArrayList folgendermaßen aussehen:

public class Weapon
{
  public String bezeichnung;
 
  public Weapon( String bezeichnung )
  {
    this.bezeichnung = bezeichnung;
  }
}

Die Klasse Spieler erhält nun ein Attribut weapons von Typ ArrayList:

private ArrayList<Weapon> weapons = new ArrayList<Weapon>();

Damit sieht die Klasse wie folgt aus:

public class Spieler
{
  private ArrayList<Weapon> weapons = new ArrayList<Weapon>();
 
  public void addWeapon( Weapon w )
  {
    weapons.add(w);
  }
 
  public void listWeapons()
  {
    for(int i=0;i<weapons.size();i++){
      System.out.println( weapons.get(i).bezeichnung );}
  }
}

Zuletzt führen wir Spieler und Waffen in der Klasse Spiel zusammen:

public class Spiel{
 
public Spiel(){
 
Spieler uschi = new Spieler();
 
uschi.addWeapon( new Weapon( "Keule" ) );
uschi.addWeapon( new Weapon( "Morgenstern" ) );
uschi.listWeapons();                      // Ausgabe: Keule   Morgenstern
 
}
}



Klassenvariablen und Klassenmethoden

Bis jetzt kennen wir bei Variablen zwei Typen: Lokale Variablen, wie wir sie beispielsweise in Schleifen als Zählvariablen verwenden, und Instanzvariablen. Innerhalb der Klasse Kreis, die sozusagen den Datentyp Kreis definiert, haben wir beispielsweise Radius, Fuellfarbe oder Linienstärke als Instanzvariablen. Jede Variable wird für sich separat für eine Instanz (also ein aus der Klasse erzeugtes Objekt) angelegt, d.h. jede Instanz hat eine eigene Kopie der Variablen. Die Methoden beziehen sich auf die jeweils angelegte Instanz.

Damit alle Objekte einer Klasse eine bestimmte Variable gemeinsam verwenden können, gibt es die sog. Klassenvariablen. Analog dazu benutzt man Klassenmethoden für Operationen, die sich nicht auf ein bestimmtes Objekt, sondern auf die Klasse an sich beziehen. Variablen und Methoden werden mit dem Schlüsselwort static an die jeweilige Klasse gebunden. Der Zugriff auf statische Variablen erfolgt über Klassenname.Variablenname, der Zugriff auf statische Methoden über Klassenname.Methodennname

Folgendes Beispiel soll das Konzept verdeutlichen:

In einer Klasse Testobjekt wird eine statische Variable zaehler angelegt, in der die Anzahl der erzeugten Instanzen der Klasse abgespeichert wird. Über eine statische Getter-Methode kann der aktuelle Wert des Zählers abgefragt werden. In einer zweiten Klasse ZaehlerTest werden drei Instanzen der Klasse Testobjekt erzeugt und als Kontrolle ihre jeweilige Rangzahl ausgegeben. Am Ende wird über die Methode getZaehler() die Anzahl der erzeugten Instanzen abgefragt.

public class Testobjekt {
 
// Klassenvariable
    static int zaehler = 0;
 
// Instanzvariable
    int nummer;
 
    // Konstruktor 
    public Testobjekt() {
        zaehler= zaehler+1;
        nummer = zaehler;
    }
 
    // Klassenmethode
    static int getZaehler() {
       return zaehler;
    }
}
 
 
public class Zaehlertest{
 
public void zaehlerTest(){
    System.out.println("Klassenvariable zaehler:"+ Testobjekt.getZaehler());
 
        Testobjekt obj_1 = new Testobjekt();
        Testobjekt obj_2 = new Testobjekt();
        Testobjekt obj_3 = new Testobjekt();
 
        System.out.println("Instanzvariable nummer im 1. Objekt: "+ obj_1.nummer);
        System.out.println("Instanzvariable nummer im 2. Objekt: "+ obj_2.nummer);
        System.out.println("Instanzvariable nummer im 3. Objekt: "+ obj_3.nummer);
        System.out.println("Klassenvariable zaehler: " + Testobjekt.getZaehler());
 
}
}


Aufgabe 1

Erstelle wiederum ausgehend von der bekannten Klasse Schueler eine Klasse Schueler, in der die Zahl der erzeugten Instanzen festgehalten und über eine Getter-Methode ausgelesen werden kann. Schreibe nun eine zweite Klasse Schule in deren Konstruktor vier Schueler-Objekte erzeugt werden und durch den Aufruf der Getter-Methode die Anzahl der erzeugten Instanzen der Klasse Schueler angezeigt wird.


Aufgabe 2

Löse das obige Problem mit Hilfe eines Arrays in der Klasse Schule. Entferne die Ausgabe-Methode aus der Klasse Schueler und verlege die Ausgabe in eine Methode schuelerAusgeben() in der Klasse Schule. Zusätzlich soll eine Methode klassenstaerke() erstellt werden, die nach Eingabe der gewünschten Klasse deren Stärke auf der Konsole ausgibt. Hinweis: Um einen Zugriff auf die Attribute der Schueler-Objekte zu erlauben, sind Getter-Methoden in der Klasse Schueler notwendig.


Aufgabe 3

Erstelle eine Klasse Kasse mit einem Konstruktor, der nur den Namen der Klasse und den Betrag für die Einnahmen enthält. Mit Hilfe einer Klassenvariable einnahmen sollen die Gesamteinnahmen der erzeugten Kassen-Objekte festgehalten werden. Eine entsprechende Getter-Methode erlaubt den Zugriff auf den Wert der Klassenvariable. Eine weitere Klasse Abrechnung enthält nur eine Methode gesamteinnahmen(), die unter Rückgriff auf die Klassenvariable von Kasse die Gesamteinnahmen der erzeugten Kassen-Objekte auf der Konsole ausgibt.


Aufgabe 4

Für eine Firma soll ein Programm zur Abwicklung ihrer Aufträge erstellt werden. Die Grundlage ist folgendes Klassendiagramm:

Aufgabe 7-4-4.PNG

Ziel ist die Ausgabe einer Bestellbestätigung folgender Art:

Sehr geehrter Herr Huber,
wir bestätigen ihre Bestellung von 5 St. Dachziegel
zum Stückpreis von 3,59 Euro.
Der für Sie zuständige Sachbearbeiter ist Heribert Freundlich.
Ihre Auftragsnummer: 1/2015.

Hinweis: Um eine Instanz der Klasse Auftrag zu erzeugen, müssen die zugehörigen Instanzen der Klassen Kunde, Ware und Mitarbeiter erzeugt werden.