Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 U08 Entwurfsmuster (II) Inhalt der Übung ¾ Diskussion und Implementierung von Entwurfsmustern Übungsaufgaben Aufgabe 1 (Queue) Gegeben ist das folgende Analysemodell einer Warteschlange (Queue): Eine Warteschlange (Queue) besteht aus einer geordneten Reihenfolge von Objekten (Object). Objekte werden nach dem FIFO-Prinzip („first-in, first out“) in eine Warteschlange als letztes Element aufgenommen (add()) und als erstes Element entfernt (remove()). front() stellt das erste Element der Warteschlange bereit. isEmpty() testet, ob die Warteschlange leer ist. size() ermittelt die Anzahl der Objekte in der Warteschlange. Es können zwei Arten von Warteschlangen erzeugt werden (siehe Klassendiagramm unten): ¾ Warteschlangen ohne duplizierte Objekte (d.h., ein Objekt kann sich nur genau einmal in der Warteschlange „anstellen“ Æ withDuplicates = false) ¾ Warteschlangen mit duplizierten Objekten (d.h., ein Objekt kann sich mehrfach in der Warteschlange „anstellen“ Æ withDuplicates = true) Dementsprechend wird zur Implementation entweder ¾ eine Liste ohne duplizierte Objekte („geordnete Menge“ Æ OrderedSet, nicht im Java-Collection-Framework enthalten, erbt von java.util.List und java.util.Set ) oder ¾ eine „herkömmliche“ Liste (hier java.util.LinkedList) benutzt. Seite 1 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Seite 2 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Um die Warteschlange entsprechend dem gegebenen Modell zu implementieren, brauchen Sie (wie zuvor erklärt) eine geordnete Menge. Da im Java-CollectionFramework eine solche Datenstruktur nicht verfügbar ist, müssen Sie diese zunächst selbst implementieren (Klasse OrderedSetImpl). Gegeben ist dazu das Interface OrderedSet.java. Die Erläuterung der Methoden ist im Java-Code von OrderedSet.java enthalten. Lösen Sie folgende Teilaufgaben: ¾ Welche Entwurfsmuster sind im Modell berücksichtigt? Diskutieren Sie diese Entwurfsmuster und zeichnen Sie sie in UML-Notation in das Klassendiagramm ein! ¾ Implementieren und testen Sie! (Praktomat: Queue) Hinweise: o Um die Warteschlange entsprechend dem gegebenen Modell zu implementieren, brauchen Sie (wie zuvor erklärt) eine geordnete Menge. Da im Java-Collection-Framework eine solche Datenstruktur nicht verfügbar ist, müssen Sie diese zunächst selbst implementieren (Klasse OrderedSetImpl). Gegeben ist dazu das Interface OrderedSet.java. Die Erläuterung der Methoden ist im Java-Code von OrderedSet enthalten. o Jetzt können Sie mit Hilfe der Datenstruktur „geordnete Menge“ eine Warteschlange implementieren (QueueImpl)! Gegeben ist dazu das Interface Queue.java. Seite 3 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Aufgabe 2 (Vehicle Queue) Gegeben ist ein Entwurfsmodell für die Simulation einer Verkehrs(warte)schlange (VehicleQueue) an einer (Rot-Grün-)Ampel. Fahrzeuge (Vehicle) in dieser Schlange können Busse (Bus), Autos (Car) und Fahrräder (Bicycle) sein. Die Methode createVehicle()der Klasse VehicleGenerator erzeugt per Zufallsgenerator (randomGenerator) entweder einen Bus, ein Auto oder ein Fahrrad. In der Verkehrsschlange stellen sich Fahrzeuge (enter()) mit einer zu definierenden Häufigkeit pro Sekunde (vehiclesPerSecondEnter) an. Falls die Ampel auf grün steht (greenLight == true), verlassen Fahrzeuge die Schlange (leave()) mit einer ebenfalls zu definierenden Häufigkeit (vehiclesPerSecondLeave). Die Variable trafficLightRate gibt die Zeitdauer einer Rot- als auch Grünphase der Ampel in Sekunden an. Initialisiert ist die Ampel mit rot (greenLight == false). Die Methoden getLength() und getSize() berechnen die Länge der Verkehrsschlange in Metern bzw. in Anzahl von Fahrzeugen. Die Zeit wird durch ein Time–Objekt simuliert. Time kennt die aktuelle Zeit in Sekunden (currentTime) sowie eine Zeitbegrenzung in Sekunden (endOfTime). Die Methode run() zählt (immer mit 0 beginnend) die Zeit sekundenweise bis zur Zeitbegrenzung hoch. Der folgende Codeausschnitt aus der Klasse Simulation demonstriert beispielhaft die Anwendung der Klassen Time und VehicleQueue. public class Simulation { static Time myTime . . . public static void main (String args[]) { VehicleQueue queue = new VehicleQueue(0.1, 30, 0.5, new VehicleGenerator()); . . . myTime.initEndOfTime(70); myTime.run(); } } Lösen Sie folgende Teilaufgaben: ¾ Überdenken Sie den gegebenen Entwurf der Klasse Time, indem Sie die Klasse zu einem Singleton verändern. Ergänzen Sie dementsprechend die Klasse im UMLKlassendiagramm! ¾ Implementieren Sie die Klassen VehicleQueue und Time! Nach jeder Sekunde sollen entsprechend der oben beschriebenen Variablen Fahrzeuge die Schlange betreten als auch verlassen. Nutzen Sie zur Simulation (Simulation.java) des Sekundentaktes das Observer-Entwurfsmuster! Gegeben sind die Klassen Vehicle.java, VehicleGenerator.java, Bus.java, Bicycle.java und Car.java. Seite 4 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 ¾ Welches weitere Entwurfsmuster (neben Singleton und Observer) ist im Entwurf enthalten? ¾ Zeichen Sie die Entwurfsmuster in UML-Notation in das nachfolgende Klassendiagramm ein! ¾ Implementieren Sie das Modell und testen Sie! (Praktomat: Vehicle Queue)! Seite 5 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Aufgabe 3 (Pricing) Der folgende Entwurf (siehe Klassendiagramm) modelliert eine komplexe Preisbildungslogik, die insbesondere verschiedene Rabatte ermöglicht. Grundsätzlich gibt es zwei Rabattarten: ¾ Prozentualer Rabatt auf den Listenpreis (Klasse PercentageDiscountPricing). Der gewährte Prozentsatz percentage wird dem Konstruktor übergeben (0 <= percentage < 100). ¾ Absoluter Rabatt auf den Listenpreis (Klasse AbsoluteDiscountPricing). Der absolute Rabatt discount wird nur für den Listenpreis oberhalb eines Mindesteinkaufspreises (threshold) gewährt. Der Mindesteinkaufspreis darf auch nach Anwendung eines Rabattes nicht unterschritten werden. Beide Parameter werden dem Konstruktor übergeben. Die Klasse Sale berechnet für einen Verkaufsvorgang ausgehend von einem Listenpreis (preDiscountTotal) den Preis für den Kunden unter Gewährung von Rabatten (Methode getTotal()). Die eigentliche Rabattberechnung wird von einem ISalePricing-Objekt vorgenommen, dessen Methode getTotal() dazu ein Sale-Objekt übergeben wird. Nun kann es sein, dass gleichzeitig verschiedene Rabatte angewendet werden können (z.B. Rabatt für Senioren oder andere spezielle Kunden, Treuebonus, Tagesrabatte). Da Rabatte in unserer Anwendung nicht kumuliert werden können, ist ein Rabatt für die Preisbildung auszuwählen. Hier kommt eine komplexe Preisbildungslogik (Klasse ComplexPricing) zur Anwendung. Dabei gibt es zwei Alternativen: ¾ Die Klasse BestForCustomerPricing berechnet den Kundenpreis so, dass der für den Kunden höchste Rabatt ausgewählt und angewendet wird. ¾ Die Klasse BestForStorePricing berechnet den Kundenpreis so, dass der für den Verkäufer günstigste Rabatt ausgewählt und angewendet wird. Bevor für einen Verkaufsvorgang der Kundenpreis berechnet wird, müssen alle für den Kunden anwendbaren Rabatte ausgewählt und der verwendeten Preisbildungslogik bekannt gemacht werden. Die Rabatte werden mit der Methode createPricing() der Klasse Sale ausgewählt. Die Parameter dieser Methode sind ¾ discountType: Die zulässigen Werte für den prozentualen und den absoluten Rabatt sind durch das Interface DiscountType definiert. ¾ percentage: Im Fall des prozentualen Rabattes wird damit der zu gewährende Prozentsatz übergeben. Die beiden restlichen Parameter sollten auf 0 gesetzt werden. ¾ discount und threshold: Im Fall des absoluten Rabattes beschreiben diese beiden Parameter wie oben erläutert den zu gewährenden Rabatt. Der Parameter percentage sollte auf 0 gesetzt werden. Zum Schluss wird die anzuwendende Preisbildungslogik myPricing (BestForCustomerPricing oder BestForStorePricing im komplexen Anwendungsfall) mit der Methode setPricing() festgelegt. Die nachfolgende Klasse SaleApp.java illustriert die Anwendung dieser Methoden: Seite 6 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 public class SaleApp implements DiscountType { public static void main (String[] args) { Sale sale1 = new Sale(100.0); ISalePricing p1, p2, p3, p4,p5; p1 = sale1.createPricing(PERCENTAGEDISCOUNT,10.0,0,0); p2 = sale1.createPricing(PERCENTAGEDISCOUNT,5.0,0,0); p3 = sale1.createPricing(ABSOLUTEDISCOUNT,0,5,50); p4 = sale1.createPricing(ABSOLUTEDISCOUNT,0,30,90); p5 = sale1.createPricing(ABSOLUTEDISCOUNT,0,19,80); BestForCustomerPricing p = new BestForCustomerPricing(p1); p.add(p2); p.add(p3); p.add(p4); p.add(p5); sale1.setPricing(p); System.out.println("BestForCustomer Price = " + sale1.getTotal() + " EUR"); } } Lösen Sie folgende Teilaufgaben: ¾ Zeichnen Sie die Ihnen bekannten Entwurfsmuster in UML-Notation in das Klassendiagramm ein! ¾ Implementieren Sie das Modell und testen Sie! (Praktomat: Pricing)! Seite 7 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Empfehlung Implementieren Sie als weitere Praktomatsaufgabe Part Management! Aufgabe 4 (Desktop Search Engine) Sie haben den Auftrag, eine erste Version einer primitiven Desktopsuchmaschine (MyDesk) zu entwickeln. Dabei geht es darum, dass (auf einem Rechner) nicht nur nach Dateien eines bestimmten Namens gesucht werden kann, sondern auch nach Dateien mit einem bestimmten Dateiinhalt. Basierend auf der Annahme, dass üblicherweise viele Suchanfragen hintereinander gestellt werden, soll nicht für jede Anfrage das Dateisystem neu durchsucht werden müssen. Stattdessen wird ein Index erstellt. Mit der Registrierung jeder Datei im Index wird dieser um neue Schlüsselwörter und die dazugehörige Datei erweitert. Als eine besondere Schwierigkeit stellt sich dabei heraus, dass der Inhalt einer Datei natürlich sehr stark von ihrem Dateityp abhängt. Ein Textdokument beispielsweise bietet sehr viel Information, die sich gut aufbereiten lässt; bei einer Grafik sieht das ganz anders aus. Hier ist es im Allgemeinen nicht möglich, passende Schlüsselworte abzuleiten. Seite 8 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Für einen ersten Entwurf lassen sich folgende Klassen bzw. Interfaces identifizieren (siehe Klassendiagramm): ¾ Von zentraler Bedeutung ist die Klasse Resource. Sie stellt eine konkrete Datei im Index dar. Dazu soll der Dateiname gespeichert werden (name), der Pfad zu der Datei (path), und ein Verweis auf einen passenden Datei- bzw. Ressourcentyp (rt). ¾ Der Typ einer Datei wird durch die Klasse ResourceType dargestellt. Sie hat nur zwei Attribute. Zum einen speichert sie eine Kurzbeschreibung (desc), welche den Dateityp knapp beschreibt, z.B. "JPG-Graphik", zum anderen verweist sie auf einen KeywordCollector (collector). ¾ Das Interface KeywordCollector stellt eine Methode getKeywords()bereit, die für die Indexerstellung gebraucht wird. Sie ermittelt zu einer Resource die Menge ihrer Schlüsselworte. ¾ Eine Klasse, die das Interface KeywordCollector standardmäßig implementiert, ist der DefaultCollector. Er soll lediglich den Dateinamen als Schlüsselwort zurück- geben. Der Sinn dieser Klasse ist es, für Dateien unbekannten Typs wenigstens den Dateinamen als Schlüsselwort verwenden zu können. ¾ Die Klasse PlainTextCollector als weitere Implementierung von KeywordCollector berechnet mit Hilfe des TextFileIterators zu einer Textdatei die Menge der in ihr enthaltenen Worte als Schlüsselworte. Dazu wandelt der TextFileIterator den gesamten Inhalt einer Textdatei in einen String um (getAsString()). Der TextFileIterator implementiert das Interface java.util.Iterator. Er soll nacheinander alle Wörter der Textdatei zurückliefern. Wörter werden durch alle Zeichen voneinander getrennt, die nicht Buchstaben oder Ziffern sind. Als zusätzliche Besonderheit sollen getrennte Wörter erkannt und wieder zusammengefügt werden. Eine Worttrennung liegt dann vor, wenn auf die Zeichensequenz Bindestrich/Zeilenumbruch („-\n“) direkt ein Kleinbuchstabe folgt. ¾ Die Klasse MyDesc schließlich stellt die Funktionalität zur Verfügung, die für die Desktopsuchmaschine gebraucht wird. Dazu gehört neben der Registrierung eines neuen Dateityps (registerType()) das Hinzufügen einer Datei zum Index (registerResource()) und das Bearbeiten einer Suchanfrage (processRequest()). ¾ Der Index der auf dem Desktop registrierten Dateien wird in der Klasse Index verwaltet. Er stellt eine Abbildung von Schlüsselwörtern auf Dateien dar (pro Schlüsselwort eine Liste von Dateien). Die zu einer Datei gehörigen Schlüsselwörter wurden über den zugehörigen KeywordCollector ermittelt. Es ist möglich, eine Datei in den Index aufzunehmen (add()) und sich die Liste von Dateien geben zu lassen, die mit einem bestimmten Schlüsselwort indiziert sind (getResources()). Seite 9 von 10 Dr. Birgit Demuth, Lehrstuhl Softwaretechnologie, LV Softwaretechnologie, WS 2007/08 Lösen Sie folgende Aufgaben: a) Welche (zwei) Entwurfsmuster erkennen Sie in dem Modell? Zeichnen Sie diese in das Klassendiagramm ein! Ergänzen Sie dazu das Modell um ggf. fehlende Klassen/Interfaces. b) Implementieren und testen Sie (Praktomat: Desktop Search Engine). Seite 10 von 10