Universität der Bundeswehr Fakultät für Informatik Institut 2 Priv.-Doz. Dr. Lothar Schmitz FT 2006 Zusatzaufgaben Lösungsvorschlag Objektorientierte Programmierung Lösung 22 (Java und UML-Klassendiagramm) a) Im folgenden UML-Diagramm sind teilweise auch Methoden und Attribute vorhanden, die erst an späterer Stelle in der Aufgabe vorkommen sowie Zugriffsrechte und Datentypen. Dies war laut Aufgabenstellung nicht gefordert, soll hier aber der Vollständigkeit dienen b) 1. In der Klasse VorlesungsTeilnahme muss die Methode wie folgt implementiert sein: public int gebuehren() { return 30 + (anzahlbesuchteruebungen * 10); 2. Die Unterscheidung wird durch Polymorphie erreicht. Das heißt im einzelnen, dass bei der Klasse Teilnahme die Unterklassen VorlesungsTeilnahme und SeminarTeilnahme die abstrakte Methode gebuehren unterschiedlich implementieren. In der Unterklasse Gasthoerer wird die konkrete Methode druckegebuehren reimplementiert. 1
c) Die Ermittlung des fleißigsten Studenten kann z.b. durch folgenden Code in den erwähnten Klassen implementiert werden: In der Klasse Verwaltung: public Student findeprimus() { Iterator keyit = studentendaten.keyset().iterator(); //Vorbelegen mit "erstem" Studenten, falls überhaupt //Studenten vorhanden. Student primus; if(keyit.hasnext()) { primus = (Student)studentenDaten.get(keyIt.next()); //Ermittlung des Primus, indem stets der Student in der Variable //primus gemerkt wird, der bis jetzt die meisten Wertungspunkte //hatte. Dieser wird dann mit dem "nächsten" verglichen und event. //ausgetauscht. ACHTUNG: Wenn zwei Studenten gleich viele //Wertungspunkte haben, ist der der Primus, der bzgl. des Iterator //als erster erscheint! while(keyit.hasnext()) { Student aktstud = (Student)studentenDaten.get(keyIt.next()); if( primus == null primus.wertungspunkte() < aktstud.wertungspunkte() ) { primus = aktstud; return primus; In der Klasse Student: public int wertungspunkte() { int punkte = 0; Iterator keyit = teilnahmedaten.keyset().iterator(); //Die Wertungspunkte aller Teilnahmen werden aufaddiert. while(keyit.hasnext()) { punkte += ((Teilnahme)teilnahmeDaten.get(keyIt.next())).wertungspunkte(); return punkte; In der Klasse SeminarTeilnahme: public int wertungspunkte() { return 4; In der Klasse VorlesungsTeilnahme: public int wertungspunkte() { return anzahlbesuchteruebungen; 2
d) Die Implementierung verwendet eine statisch innere Klasse, die in der Klasse Verwaltung definiert wird. Die eigentliche Arbeit wird von der Methode nextworker erledigt, auf die sich next und der Konstruktor stützen. Das Problem bei der Implementierung ist, dass next nicht null zurückgeben darf, wenn kein Objekt mehr im Iterator vorhanden ist (laut dem Vertrag den das Interface java.util.iterator einer Implementation auferlegt). In diesem Fall muss eine java.util.nosuchelementexception geworfen werden. Des weiteren darf hasnext beliebig oft vor einem next aufgerufen werden, d.h. mit anderen Worten, dass hasnext nicht den Zustand des Objekts verändern darf (deshalb kann die Arbeit, die nextworker verrichtet nicht von dieser Methode erledigt werden). Die hasnext Methode muss aber wirklich implementiert werden und kann nicht einfach an die entsprechende Methode des studit delegiert werden, da noch gefiltert werden muss. Die Idee ist, immer schon im voraus zu ermitteln, welches Objekt vom nächsten Aufruf der Methode next zurückgegeben wird. Dieses wird in nextobject gemerkt. Wenn kein Objekt mehr zurückgegeben werden kann, ist nextobject == null. Der Wert von nextobject wird rekursiv in der Methode nextworker ermittelt. Da die Implementierung von remove in diesem Fall nicht sinnvoll erscheint, wird einfach eine java.lang.unsupportedoperationexception geworfen. Das ist laut Java-API-Referenz der richtige Weg, sich um diese Implementierung zu drücken. Die angegebenen Code-Stücke stammen nur aus der Klasse Verwaltung: public class LvIterator implements Iterator { Iterator studit; String veranstaltungsnummer; Object nextobject; LvIterator(Lehrveranstaltung lv) { studit = studentendaten.keyset().iterator(); veranstaltungsnummer = lv.veranstaltungsnummer; //nextworker benutzt schon studit und veranstaltungsnummer!! nextobject = nextworker(); private Object nextworker() { //ACHTUNG: nextobject noch nicht initialisiert beim ersten // Aufruf! if(studit.hasnext()) { Student aktstud = (Student)studIt.next(); if(aktstud.teilnahmedaten.containskey(veranstaltungsnummer)) { return aktstud.teilnahmedaten.get(veranstaltungsnummer); else { return nextworker(); else { return null; public boolean hasnext() { return nextobject!= null; public Object next() { if(!hasnext()) { throw new NoSuchElementException(); Object resultobject = nextobject; 3
nextobject = nextworker(); return resultobject; public void remove() { throw new UnsupportedOperationException(); public Iterator iterator(lehrveranstaltung lv) { return new LvIterator(lv); Lösung 23 (UML Klassendiagramm - Fakultät) 4
Lösung 24 (Bäume als Datenstrukturen und UML-Diagramme) a) b) public class LeererBaum extends ArtikelBaum { public LeererBaum() { super(); public boolean istleer() { return true; public class WurzelBaum extends ArtikelBaum { public WurzelBaum(int schluessel, int wert, ArtikelBaum links, ArtikelBaum rechts) { this.schluessel = schluessel; this.wert = wert; this.links = links; this.rechts = rechts; 5
public boolean istleer() { return false; c) public class LeererBaum extends ArtikelBaum { public boolean enthaelt(int schluessel) { return false; public int finde (int schluessel) throws NoSuchElementException { throw new NoSuchElementException(); public class WurzelBaum extends ArtikelBaum { public boolean enthaelt(int schluessel) { if (this.schluessel == schluessel) return true; if (schluessel < this.schluessel) return links.enthaelt(schluessel); return rechts.enthaelt(schluessel); public int finde (int schluessel) { if (this.schluessel == schluessel) return wert; if (schluessel < this.schluessel) return links.finde(schluessel); return rechts.finde(schluessel); d) public class LeererBaum extends ArtikelBaum { public WurzelBaum einfuege(int schluessel, int wert) { return new WurzelBaum (schluessel, wert, new LeererBaum(), new LeererBaum()); public ArtikelBaum loesche (int schluessel) throws NoSuchElementException { throw new NoSuchElementException(); e) public class LeererBaumIterator implements java.util.iterator { public boolean hasnext() { return false; 6
public Object next() { throw new NoSuchElementException(); public void remove() { throw new UnsupportedOperationException(); f) Das Attribut position gibt den Zustand des Iterators an und nimmt nacheinander die Werte 0, 1 und 2 an. 0 bedeutet, dass noch gar kein Element iteriert wurde. 1 bedeutet, dass gerade der linke Teilbaum iteriert wird. Entsprechend bedeutet 2, dass der rechte Teilbaum iteriert wird. Wie in der Aufgabenstellung verlangt wird im Feld it der jeweils dafür benötigte Iterator gespeichert. Ist position==2 und it.hasnext()==false wurde auch der rechte Teilbaum vollständig durchsucht. Deshalb gibt hasnext() nun auch false zurück. public class WurzelBaumIterator implements java.util.iterator { private WurzelBaum baum; private int position; private ArtikelBaumIterator it; public WurzelBaumIterator( WurzelBaum baum) { this.baum = baum; position = 0; // it nicht initialisiert public boolean hasnext() { return (position < 2 (position == 2 && it.hasnext())); public Object next() { if (position == 0) { it = baum.getlinks().iterator(); position = 1; if (position == 1) { if (it.hasnext()) return it.next(); it = baum.getrechts().iterator(); position = 2; return baum; // hier ist position == 2 return it.next(); public void remove() { throw new UnsupportedOperationException(); 7
Lösung 25 (Eieruhr) 8
Lösung 26 (Zustandsdiagramm einer Digitaluhr) 9