Kapitel 4 Designmönster 4.1 Kommandomönstret Uppgift 4.1 Vi har ett interface Drawing (se nedan), och vill kunna rita olika slags figurer i ett Drawing-objekt. interface Drawing { void clear(); void useforegroundcolor(); void usebackgroundcolor(); void moveto(int x, int y); void circle(int radius); void rectangle(int width, int height); I ett Drawing-objekt finns alltid en aktuell punkt, och vi flyttar den med moveto. Metoden circle ritar en cirkel med given radie runt den aktuella punkten, rectangle ritar en rektangel med given storlek med den aktuella punkten som mittpunkt. För att rita vill vi använda Command Pattern, och vi vill både kunna rita och ångra figurer (för att ångra en figur ritar vi den i bakgrundsfärg). Så vi har följande interface: interface DrawCommand { void draw(drawing d); void undo(); 11
1. Implementera klasserna DrawCircle och DrawSquare som ritar (och ångrar) cirklar och kvadrater. Man skall kunna skapa cirkel- och kvadratritningsobjekt genom att skriva: void example(drawing d) { DrawCommand c = new DrawCircle(0, 0, 1); DrawCommand s = new DrawSquare(0, 0, 2); c.draw(d); //... woops, take back : c.undo(); Parametrarna till konstruktorerna i DrawCircle och DrawSquare är i tur och ordning x- och y-koordinaterna för mittpunkten, och radie eller sidlängd. 2. Rita ett fullständigt klassdiagram med klasserna i din lösning. Attribut behöver dock inte vara med. Uppgift 4.2 Vi vill nu koppla våra ritklasser i föregående problem till ett GUI, och vill ha ett antal Action-klasser, som beskriver användarens interaktion med programmet: interface Action { void execute(drawing d, Stack<DrawCommand> history); Här är history en stack med alla de ritkommandon som har utförts (vi behöver dem om vi skall kunna ångra dem). Klassen Stack har specifikationen class Stack<E> { public boolean isempty(); public void push(e value); public E pop(); 12
Vi kommer i uppgiften att hantera tre slags Action-klasser: DrawAction: ritar en figur man skickar med ett DrawCommand när man skapar en DrawAction, så konstrueraren har rubriken: public DrawAction(DrawCommand cmd) Undo: ångrar den senaste utritade figuren. Man skall kunna ångra hur många utritade figurer som helst, så efter att vi ångrat en figur räknas den figur vi ritade precis före den borttagna som senaste figur. Exit: avslutar programmet (med System.exit(0)). Vi har även ett interface GUI, med en metod next() som ligger och tolkar det användaren gör, och skickar tillbaka nästa action : interface GUI { Action next(); En kollega kommer att skriva en GUI-klass, och hon känner till hur våra Action-objekt skapas. 1. Implementera de tre Action-subklasserna ovan. 2. Skriv en metod med rubriken: void run(gui gui, Drawing d) { //... här s k a l l du f y l l a i din kod... Den skall hämta kommandon från GUI och uppdatera ritningen enligt användarens önskemål. 13
Uppgift 4.3 I ett program kan man ge tre kommandon: X, Y och Z. Ett kommando beskrivs av en klass som innehåller en metod execute( ) som utför kommandot. Kommandon lagras efter hand som de utförs, och man kan senare ge numret på ett lagrat kommando som skall utföras igen (1 motsvarar det senast utförda kommandot, 2 det näst senaste, osv). Detta beskrivs i sekvensdiagrammet i figur 4.1 (först lagringen av ett X-kommando, sedan utförandet av det lagrade kommandot med nummer 5 som råkar vara ett Y-kommando i detta fall): Figur 4.1: Sekvensdiagram Om parametern till metoden newcommand är Y eller Z så skapas naturligtvis ett Y- eller Z-kommando i stället för det X-kommando som visas i diagrammet. Skriv ett antal Javaklasser där all information som man kan utläsa ur den inledande beskrivningen och ur sekvensdiagrammet finns med! Använd designmönstret Command i din lösning. Du kan anta att parametern till metoden docommand() alltid har ett giltigt värde (dvs motsvarar ett existerande tidigare kommando). 14
4.2 Kompositmönstret Uppgift 4.4 Nedan finns ett program där en ritning Drawing1 bestående av figurer ritas ut på SimpleWindow. Man vill nu separera själva skapandet av ritningen från utritningen och har bestämt sig för att använda sig av Kommandomönstret. public class DrawFiguresInWindow{ public static void main(string[] args) { SimpleWindow w = new SimpleWindow(600,600,"Window"); drawdrawing1(w); public static void drawdrawing1(simplewindow w) { drawsquare(w,250,250,100); drawcircle(w,100,100,50); drawsnowman(w,200,100); public static void drawsquare(simplewindow w, int x, int y, int side) { Square sq = new Square(x,y,side); sq.draw(w); public static void drawcircle(simplewindow w, int x, int y, int radius) { Circle c = new Circle(x,y,radius); c.draw(w); public static void drawsnowman(simplewindow w, int x, int y) { Circle c = new Circle(x, y, 100); c.draw(w); c = new Circle(x, y-150, 50); c.draw(w); Square sq = new Square(x,y-150,20); %//näsa : ) sq.draw(w); 15
public class Square{ int x, y, side; public Square(int x, int y, int side){ this.x=x; this.y=y; this.side=side; public void draw(simplewindow w){ w.moveto(x,y); w.lineto(x,y+side); w.lineto(x+side,y+side); w.lineto(x+side,y); w.lineto(x,y); // Circle är analog med Square Gör om designen så att Kommandomönstret används, där Square, Circle och Snowman är olika kommando. Kompositmönstret ska användas där det är relevant. Ritningen är en lista av kommando. Presentera lösningen med ett UML-Klassdiagram. 16
4.3 Mallmetodmönstret Uppgift 4.5 Följande två klasser innehåller duplicerad kod. Elimenera den genom att använda Template Method-mönstret. Lösningen redovisas som Java-kod. public class StarList extends java.util.arraylist { public String tostring() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < size(); i++) { builder.append("* "); builder.append(get(i)); builder.append( \n ); return builder.tostring(); public class EnumList extends java.util.arraylist { public String tostring() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < size(); i++) { builder.append(i + 1).append(". "); builder.append(get(i)); builder.append( \n ); return builder.tostring(); 17
4.4 Strategimönstret Uppgift 4.6 En mobiltelefonoperatör erbjuder olika slags abonnemang. Läraren i OMD har sagt att när beskrivningen av verkligheten använder olika slags skall man använda arv. Den uppmärksamme teknologen gör följande design på sommarjobbet på Comviq: public abstract class Tariff { private String phonenumber; protected List<Call> call = new ArrayList<Call>(); public abstract Money debit(call call); // omissions public class CashCard extends Tariff { public Money debit(call call) { // omissions public class KnockOut extends Tariff { public Money debit(call call) { // omissions Projektledaren har invändningar; när en abonnent vill byta abonnemangsform måste man skapa ett nytt Tariff-objekt och kopiera telefonnummer mm från det gamla abonnemanget. Gör en ny design utan denna olägenhet som utnyttjar designmönstret Strategy. Redovisa designen med ett klassdiagram. 18
Uppgift 4.7 Använd Strategimönstret för att konstruera en klass FilteredList, som innehåller en lista av strängar och ett filter som gör att metoden tostring bara returnerar en sträng sammansatt av de strängar som filtret accepterar. Om listan innehåller [ good, morning, sir ] och man använder ett filter som accepterar strängar med minst fyra tecken skall tostring returnera good morning Om filtret accepterar alla strängar skall resultatet bli good morning sir Filtret skall vara ett attribut i klassen FilteredList. Lösningen redovisas som ett Java-program som innehåller allt som behövs för att exekvera exemplet med två utskrifter. 4.5 Null Objekt Uppgift 4.8 I följande exempel används null för att representera okända föräldrar. Använd Null Object-mönstret för att få en bättre design där det inte behövs några ifsatser. Lösningen redovisas med ett klassdiagram med generaliseringar, attribut och metoder samt Java-kod. public class Person { private String name; private Person father, mother; public void printancestors() { System.out.println(name); if (father!= null) { father.printancestors(); if (mother!= null) { mother.printancestors(); 19
Uppgift 4.9 I ett klassbibliotek finns det en abstrakt klass för att representera ljudkortet i en dator (endast för uppgiften relevanta delar visas nedan): public abstract class SoundCard { abstract String getversion(); abstract void playfile(string filename);... Vidare finns i det givna kalssbiblioteket en klass Hardware som kan användas för att få information om den installerade hårdvaran i den aktuella datorn. I denna finns bland annat en statisk operation för att skapa ett objekt som representerar ljudkortet i datorn. Beroende på vilket ljudkort som är installerat returneras ett objekt av en passande subklass till SoundCard eller null om inget ljudkort är installerat: public class Hardware { SoundCard getsoundcard();... Vi vill använda detta bibliotek i ett program för att spela upp ljud om ett ljudkort är installerat. I annat fall kör programmet under tystnad. Ett utdrag ur ett sådan program skulle kunna se ut enligt följande: // Ask library hardware class for sound card reference or // null i f soundcard not i n s t a l l e d. SoundCard card = Hardware.getSoundCard();... // Print sound card version if (SoundCard == null) { System.out.println("Sound card not available!"); else { System.out.println(card.getVersion);... // Play sound f i l e i f sound card present if (SoundCard!= null) { card.playfile("mysoundfile.snd"); Som synes leder detta till att man behöver göra många kontroller för null i sitt program. Beskriv med text och java-kod hur du kan använda NULLmönstret för att skriva om kodexemplet ovan för att lösa problemet. 20
4.6 Dekoratörsmönstret Uppgift 4.10 Med institutionens kaffeautomat kan man välja olika drycker och flera tillbehör genom att trycka på knappar. Använd Decorator-mönstret för att modellera möjligheten att välja kaffe eller choklad, med eller utan tillbehören mjölk och socker. Om man trycker flera gånger på tillbehörsknapparna ökar dosen på tillbehöret. Modellen skall testas med public static void main(string[] args) { Drink drink = new Sugar(new Milk(new Milk(new Coffee()))); System.out.println(drink); System.out.println(drink.cost()); med den förväntade utskriften coffee milk milk sugar 7 Kostnaden för en dos kaffe är 5 kronor, en skvätt mjölk kostar 1 kr och sockret är gratis. Gör ett klassdiagram för hela modellen och implementera klasserna Coffee och Milk och alla abstrakta klasser. 21