| 
 Laboration 4 - DesignmönsterDesignmönster beskriver beprövade lösningar som kan användas i
flera olika, men liknande, sammanhang inom objektorienterad programmering.
Med designmönster i sin "verktygslåda" kan designproblem lösas snabbare 
och säkrare än utan. Vissa mönster är enkla och lätta att lära sig medan
andra är lite komplicerade.
När man ska lära sig att använda ett nytt mönster praktiskt så är det 
en god idé att programmera upp mönstret på ett litet "leksaksexempel"
för att verkligen förstå hur det är tänkt att fungera. 
 
I den här labben ska ett par vanliga och populära mönster studeras
genom att små exempel programmeras.
 Mönstret CompositeIdén med mönstret Composite är att grupper av objekt ordnade hierarkiskt i en
trädstruktur ska kunna behandlas på samma sätt som enstaka objekt.
En operation på det sammansatta objektet ska utföras på objektets alla
delar.
Ett sammansatt objekt (composite object) innehåller andra objekt
som själva kan vara sammansatta eller enstaka.
Ett typiskt exempel på Composite är en filkatalog där en katalog kan
innehålla både filer och andra kataloger, som i sin tur kan innehålla 
både filer och andra kataloger o.s.v. i många nivåer. Filerna är löv i trädet.
Ett annat exempel är en bild som 
byggs upp av grafiska primitiver (linjestycken, ovaler, rektanglar m.m)
och sammansättningar av grafiska primitiver där en sammansättnng kan
innehålla både andra sammansättningar och primitiva objekt. 
I Java-biblioteket finns ett riktigt paradexempel på Composite, nämligen
klasserna Component, Container och alla dess subklasser. En Container
ärver från Component och kan innehålla många Components. Dessa 
kan själva vara Containers eller så kan de vara "löv"-komponenter som 
inte kan innehålla några andra.
 
En beskrivning av Composite som inleds med ett UML-klassdiagram finns här:www.dofactory.com/Patterns/PatternComposite.asp
 
 
 Deluppgift 1 – Resväska som CompositeNär man ska packa en resväska kan man använda sig av mönstret Composite.
Koftor och byxor lägger man direkt i resväskan medan hårspännen och 
gummisnoddar stoppas i små askar eller påsar som kanske i sin tur läggs
i en necessär eller större påse som läggs bredvid större plagg i väskan. 
Strumpor läggs i en påse som läggs i väskan.
 
Implementera de klasser som behövs för en resväska enligt mönstret Composite.
Den klass som kallas Component i mönstrets vanliga beskrivning är
en abstrakt klass från vilken klasserna för de enskilda prylarna 
och klassen eller klasserna för sammansättningarna ärver. 
Allra minst tio saker
av minst fem olika slag ska packas (t.ex. tjock tröja, jeans, strumpa, 
armband, penna ... använd fantasin!). Minst tre olika nivåer av
sammansättningar ska användas. T.ex. att hårspännen ligger i en
påse som ligger i en necessär tillsammans med tvål och schampoo
och att necessären ligger i resväskan bredvid större klädesplagg.
  Välj själva om ni vill göra olika klasser för de olika sorternas 
prylar eller använda en klass med ett String-attribut som namnger 
prylen. 
Även när det gäller behållare kan man välja att ha en enda klass för alla
eller en för Necessär, en för Påse, en för Väska o.s.v. Det blir förstås
enklast att ha en klass här också, med String-variabel som specificerar
användningen.
 
Det måste vara minst tre klasser! Se UML-diagrammet!
 
Varje pryl ska ha ett prisoch enviktsom instansvariabler. Dessa ges värden när objekten skapas genom
en standardmässig konstruktor.
Tvåoperationerska finnas (Operation()i 
allmänna beskrivningen av mönstret): De två operationerna ärpris()ochvikt()som returnerar motsvarande data.
Varje sammansättning/behållare ska ha en lista över sitt innehåll,
t.ex. en ArrayList med de komponenter den innehåller.
Vid anrop avpris()ellervikt()får den
beräkna sitt totalpris och sin totalvikt genom att gå igenom innehållet
och summera. 
Skriv ett testprogram som bygger upp en resväska.
Resväskans totala vikt ska beräknas i ett metodanrop, t.ex.
resväska.vikt()och skrivas ut, priset visas på motsvarande sätt.
Tänk på att själva väskan och packpåsar har egen vikt och pris utöver vikt
och pris för innehållet!
Kontrollera att samma värden erhålls om man anroparpris()ellervikt()utan att innehållet ändrats! 
Observera att inga explicit rekursiva metoder ska skrivas.
Det är mönstrets struktur som ser till att hela innehållet 
gås igenom då en metod anropas för något objekt i strukturen.  
 
Det är tillåtet att programmera ett annat exempel på mönstret Composite om 
ni kommer på något passande. Det måste vara minst tre nivåer och minst
tio objekt totalt.
 
 Factory-teknikDet finns flera designmönster som innehåller ordet Factory.
Gemensamt för dem är att man inte skapar objekt på det vanliga sättet
med new utan genom ett metodanrop. Metoderna ses som "fabriker"
där objekt skapas och vanliga namn på metoderna ärgetInstance()ellercreate(). Typen för det skapade objektet bestäms
i metoden utom räckhåll för klienten (användaren).
Inuti metoderna används förstås new men klienten, den som använder
klassen, har inte direkt tillgång till new.
Vanliga skäl för den här tekniken är att användaren ska slippa bry sig
om objektets typ eller att det säkrare blir rätt typ om metoden väljer
och inte användaren eller att klassen själv skall ha full kontroll över 
vilka objekt som skapas.
 
 Deluppgift 2 -- Human factoryImplementera en abstract klassHumanmed två konkreta subklasserManochWoman. IHumanska en
factory-metod finnas :
    public static Human create (String pnr) {
        // metodkropp
    }
Vi förutsätter att parametern är ett riktigt personnummer om tio siffror
och ett streck före de fyra sista (ni behöver inte kolla det). 
Om näst sista siffran är udda så ska en instans avManreturneras, om den är jämn returneras ett objekt avWoman.
Skriv ett testprogram som skapar objekt av ManochWomanmed hjälp av metoden ovan.
Se till att det inte går att skapa objekt avManochWomanpå annat sätt än genom metoden ovan. Det ska
alltså inte gå att kompileranew Man(...)ellernew Woman(...)i testprogrammet. Det får inte heller gå
att skapa en anonym subklass tillHuman. 
Tips: Gör ett paket humansom innehållerHumanoch dess subklasser. Paketet läggs på en
underkatalog med samma namn. Med hjälp av
lämpliga modifierare för synlighet (public, private m.fl.) kan man
åstadkomma den begärda strukturen. Lägg testprogrammet på
katalognivån ovanför paketet. Se också till att testprogrammet
på något sätt bekräftar att objekt av rätt typ skapats, t.ex.
genom en metodtoString()som skriver ut information
om objekten. Exempel: 
    :
    Human anna = Human.create("Anna", "xxxxxx-012x");
    Human magnus = Human.create("Magnus","xxxxxx-011x");
    System.out.println(anna);
    System.out.println(magnus);
kan ge utskriften
    Jag är kvinna och heter Anna
    Jag är man och heter Magnus
 Redovisning och krav på programmen
 Composite-mönstret ska implementeras med minst tre klasser, en
sammanhållande abstrakt klass, en för behållare och en för 
icke-behållare.
 Demonstrera en körning av Composite-test-programmet där total vikt 
och totalt pris för resväskan (eller motsvarande struktur) skrivs ut. 
 Som vanligt ska ett UML-klassdiagram över klasserna visas upp. 
 Rita även upp ett objektdiagram över de objekt som Composite-
testprogrammet skapar. Diagrammet behöver inte följa UML-standard men
tydligt visa vilka objekt som finns. Rita diagrammet som ett träd! 
 Demonstrera en körning av Human-factory-programmet där en man och
en kvinna skapas. 
Demonstrera en annan version av programmet där 
man försöker med new Man(...)ochnew Woman(...)men kompilering misslyckas. 
 
 Extrauppgift - Mönstret IteratorSyftet med mönstret är att iterera eller "gå igenom" alla element ur en
sammansatt struktur utan man behöver befatta sig med den underliggande 
strukuren. Att använda en iterator på en lista är naturligt och
implementationen av en listiterator är ofta enkel.
Även för mer komplicerade strukturer har man god nytta av iteratorer.
Composite-sammansättningar och andra trädstrukturer kan mycket väl förses
med iteratorer. Iteratorn "levererar" elementen i sekvens utan att man
behöver veta hur strukturen är uppbyggd. 
"Man" är här den som använder iteratorn. Den som programmerar iteratorn måste
förstås känna till strukturen. Naturliga val för genomgångar av
en trädstruktur är t.ex. pre-, in- och post-order eller den ordning
som fås vid s.k. bredden-först-sökning.
 
Lite mer om iteratormönstret finns här:www.dofactory.com/Patterns/PatternIterator.asp
 Två (förenklade) iteratorer över en CompositeUppgiften här är att implementera två iteratorer på
den Composite-struktuer som gjordes i labbens första del.
De två iteratorerna ska gå igenom elementen i preorder
respektive bredden-först-ordning. Preorder betyder att roten
besöks först, därefter alla delträd från vänster till höger,
använd preorder på varje träd.
I bredden-först tar man först roten, sedan alla rotens barn, därefter
alla rotens barnbarn o.s.v.
Iteratorerna ska implementera följande interface (eller ett snarlikt):
 
public interface Iterator {
    public Object next();  // gå fram ett steg och returnera nästa element
    boolean hasnext();     // returnera true när alla element är besökta
}
Skriv två klasser som implementerar interfacet, en för preorder-genomgång
och en för bredden-först-genomgång av Composite-strukturen.
All åtkomst av Composite-elementen måste göras från Iteratorklasserna.
Därför är det en god idé att ge Iteratorklasserna en konstruktor som
tar ett Composite-objekt (enligt labbens del 1) som parameter.
Att skapa själva iterator-objekten är en uppgift för Composite-klassen
som alltså ska utvidgas med en metod liknande
 
    Iterator getIterator (String s) {
        if (s.equals("PO"))
            return new POIterator(this); //Preorder-iterator
        else
            return new BFIterator(this); //Breadth-first-iterator
    }
Metoden närmast ovan är den enda ändring/utvidgning som är tillåten i de
ursprungliga Composite-klasserna!
Ev. utvidgningar som är naturliga
delar av klasserna och som kunde ha gjorts från början är tillåtna.
T.ex. går det bra att lägga till toString()-metoder om
sådana saknas. 
 Förenklad iteratorEn riktigt generell iterator "tål" att den struktur som itereras över
ändras något under iterationen. Det behöver ni inte klara av här!
Iteratorn får förutsätta att Composite-objektet är likadant
under hela iterationen. Om objekt läggs till eller tas bort under
iterationen så duger det att iteratorn visar hur Composite-objektet
såg ut då iteratorn skapades.
 LabbredovisningTestning av de nya iteratorerna går bra att göra i samma program som
demonstrerar Composite-strukuren då labbens första del redovisas.
Skapa de två iteratorerna, iterera var och en och kontrollera genom 
någon utskrift att alla element gås igenom och att de kommer i den
förväntade ordningen. |