Objektorientierte Programmiertechniken
LVA 185.A01, VU, 3 ECTS, 2016/2017 W
| Ausgabe: | 16.11.2016 |
| Abgabe: | 23.11.2016, 12:00 Uhr |
Folgendes Interface für ein elektronisches Nahrungsmitteletikett ist vorgegeben:
public interface SquirrelFood {
int eatWithinDays(); // olfactory test required if == 0,
// uneatable if < 0
double carb(); // fraction of carbon
double fat(); // fraction of fat
double protein(); // fraction of protein
}
Folgende Unterarten von Lebensmitteletiketten sind vorgesehen:
DurableSquirrelFood werden Nahrungsmittel versehen, die sich zur Winterbevorratung eignen.
Die Methode toBeBuried liefert einen Wahrheitswert, der besagt, ob ein Vergraben des Nahrungsmittels zur Bevorratung empfohlen wird.
Der von eatWithinDays zurückgegebene Wert bezieht sich auf eine entsprechende Lagerung, also je nach toBeBuried vergraben oder nicht vergraben.
PerishableSquirrelFood werden leichtverderbliche Nahrungsmittel versehen, die nicht zur Winterbevorratung geeignet sind.
VegetarianSquirrelFood werden Nahrungsmittel versehen, die nicht tierischen Ursprungs sind.
Obwohl Eichhörnchen gerne auch Weichtiere und Insekten am Speiseplan haben, werden Nahrungsmittel tierischen Ursprungs generell nicht als zur Winterbevorratung geeignet angesehen.
Fruit werden alle Arten von Früchten, Beeren, Nüssen, Zapfen, Samen und Fruchtkörpern von Pilzen versehen, egal ob frisch oder getrocknet.
Die beiden Methoden fromMonth und toMonth liefern je eine ganze Zahl zwischen 1 und 12, welche die Monate beschreiben, in denen Nahrung dieser Art üblicherweise in freier Natur zu finden ist.
DryFruit werden durch Trocknen haltbar gemachte (in frischem Zustand leichtverderbliche) Früchte versehen, die sich auch zur Winterbevorratung eignen, aber im Freien (nicht in der Erde vergraben) gelagert werden sollen.
FreshFruit werden leichtverderbliche frische Früchte versehen.
Die Methode dry liefert ein Etikett vom Typ DryFruit zurück, das dieselbe Frucht nach Entziehung eines Großteils des Wassers beschreibt; das heißt, die Verhältnisse zwischen Kohlehydraten, Fetten und Proteinen bleiben relativ zueinander gleich, aber die Haltbarkeit wird wesentlich erhöht.
OpenSeam werden Pflanzensamen (aber nicht Früchte, auch wenn sie Samen enthalten) versehen, die nicht mit einer harten Schale oder Schutzhülle ausgestattet sind oder bei denen die harte Schale oder Hülle schon entfernt wurde.
Diese Samen eignen sich zur Winterbevorratung, wobei die Form der Lagerung (vergraben oder nicht) von der Art des Samens abhängt.
Nut werden nicht nur ganze Nüsse mit harter Schale, sondern auch die Kerne (Steine) von Steinfrüchten versehen, jedoch nicht die Steinfrüchte selbst.
Alle Nüsse und Steine eignen sich zur Winterbevorratung und sollten zur Lagerung vergraben werden.
Die Methode crack liefert ein Etikett vom Typ OpenSeam zurück, das dieselbe Nuss bzw. denselben Kern ohne harte Schale beschreibt.
Da die Schale keinerlei Nährstoffe enthällt, bleiben die Verhältnisse zwischen Kohlehydraten, Fetten und Proteinen gleich, aber die Haltbarkeit wird verringert, und die Lagerung durch Vergraben ist möglicherweise nicht mehr sinnvoll.
Cone werden Zapfen von Nadelbäumen versehen.
Sie eignen sich zur Winterbevorratung und brauchen zur Lagerung nicht vergraben werden.
Die Methode seams gibt die Zahl der Samen zurück, die (noch) im Zapfen enthalten sind.
Die Methode crack holt einen Samen aus dem Zapfen und gibt ein Etikett vom Typ OpenSeam zurück, das den herausgeholten Samen beschreibt, der vergraben gelagert werden soll.
Die Verhältnisse zwischen Kohlehydraten, Fetten und Proteinen bleiben ebenso gleich wie die Haltbarkeit.
Versehen Sie alle Ihre (abstrakten) Klassen und Interfaces mit den notwendigen Zusicherungen und stellen Sie sicher, dass Sie nur dort eine Vererbungsbeziehung (extends oder implements) verwenden, wo tatsächlich eine Untertypbeziehung auch hinsichtlich der Zusicherungen besteht. Ermöglichen Sie Untertypbeziehungen zwischen allen diesen Typen, außer wenn Untertypbeziehungen den Beschreibungen der Typen widersprechen würden.
Falls zwischen zwei Typen keine Untertypbeziehung besteht, geben Sie in einem Kommentar in der Datei Test.java eine Begründung dafür an.
Bitte geben Sie eine textuelle Begründung; auskommentierte Programmzeilen reichen dafür nicht.
Sie können so viele zusätzliche (abstrakte) Klassen und Interfaces einführen, wie Sie als vorteilhaft erachten. Die Typstruktur soll trotzdem möglichst einfach und klein bleiben, wobei jedoch alle oben genannten Typen (mit den vorgegebenen Namen) vorkommen müssen, auch solche, die Sie vielleicht für nicht nötig erachten.
Schreiben Sie eine Klasse Test zum Testen Ihrer Lösung.
Erzeugen Sie Instanzen der oben genannten Klassen, die Sie beispielsweise zu Ernährungsplänen für Eichhörnchen zusammenfassen.
Überprüfen Sie so gut Sie können mittels Testfällen, ob dort, wo Sie eine Untertypbeziehung annehmen, Ersetzbarkeit gegeben ist.
Daneben soll die Klasse Test.java als Kommentar eine kurze, aber verständliche Beschreibung der Aufteilung der Arbeiten auf die einzelnen Gruppenmitglieder enthalten – wer hat was gemacht.
| Untertypbeziehungen richtig erkannt und eingesetzt, fehlende Untertypbeziehungen richtig beschrieben | 50 Punkte |
| Zusicherungen richtig und sinnvoll eingesetzt | 25 Punkte |
| Lösung sinnvoll getestet | 10 Punkte |
| Lösung vollständig (entsprechend Aufgabenstellung) | 15 Punkte |
Obwohl für das Testen der Lösung nur 10 Punkte veranschlagt sind, kann unzureichendes Testen doch zu größerem Punkteverlust führen, wenn dadurch bestehende Mängel nicht rechtzeitig erkannt werden. Auch Mängel in der Funktionalität können einen Verlust von deutlich mehr als 15 Punkten bedeuten, weil diese sehr wahrscheinlich auch aus Fehlern in Untertypbeziehungen und Zusicherungen resultieren.
Die größte Schwierigkeit liegt darin, alle Untertypbeziehungen zu finden und Ersetzbarkeit sicherzustellen.
Vererbungsbeziehungen, die nicht gleichzeitig auch Untertypbeziehungen sind, führen zu sehr hohem Punkteverlust.
Ebenso gibt es hohe Punkteabzüge für nicht wahrgenommene Gelegenheiten, Untertypbeziehungen zwischen den Untertypen von SquirrelFood herzustellen, sowie für fehlende oder falsche Begründungen für nicht bestehende Untertypbeziehungen.
Geeignete Begründungen wären etwa Gegenbeispiele.
Eine Grundlage für das Auffinden der Untertypbeziehungen sind gute Zusicherungen. Wesentliche (aber nicht alle) Zusicherungen kommen in obigen Beschreibungen vor. Untertypbeziehungen ergeben sich aus den erlaubten Beziehungen zwischen Zusicherungen in Unter- und Obertypen. Es hat sich als günstig erwiesen, alle Zusicherungen, die in einem Obertyp gelten, im Untertyp bei den betroffenen Methoden nochmals hinzuschreiben, da sie sonst leicht übersehen werden.
Vergewissern Sie sich der Korrektheit der Untertypbeziehungen zusätzlich über Testfälle. Die Anzahl der Testfälle ist nicht entscheidend, wohl aber deren Qualität: Testfälle sollen Verletzungen der Ersetzbarkeit aufdecken können. Gegenbeispiele stellen sicher, dass keine Untertypbeziehungen übersehen wurden.
Zusicherungen in Testklassen werden aus praktischen Überlegungen bei der Beurteilung nicht berücksichtigt.
Zur Lösung dieser Aufgabe müssen Sie Untertypbeziehungen und den Einfluss von Zusicherungen auf Untertypbeziehungen im Detail verstehen. Holen Sie sich entsprechende Informationen aus Kapitel 2 des Skriptums. Folgende zusätzlichen Informationen könnten hilfreich sein:
Lassen Sie sich von der Form der Beschreibung nicht täuschen. Daraus, dass die Beschreibung eines Typs die Beschreibung eines anderen Typs teilweise wiederholt, folgt noch keine Ersetzbarkeit. Sie sind wahrscheinlich auf dem falschen Weg, wenn es den Anschein hat, A könne Untertyp von B und B gleichzeitig Untertyp von A sein – außer wenn A und B gleich sind.
Achten Sie auf die Sichtbarkeit. Alle oben beschriebenen Typen und Methoden sollen überall verwendbar sein. Die Sichtbarkeit von Implementierungsdetails soll so stark wie möglich eingeschränkt werden. Sichtbare Implementierungsdetails beeinflussen die Ersetzbarkeit.
Schreiben Sie nicht mehr als eine Klasse in jede Datei (ausgenommen geschachtelte Klassen), halten Sie sich an übliche Namenskonventionen (Großschreibung für Namen von Klassen und Interfaces, kleine Anfangsbuchstaben für Variablen und Methoden, etc.), und verwenden Sie die Namen, die in der Aufgabenstellung vorgegeben sind.