Laboration 3

Designmönster och ett litet Brevsystem

1 Inledning

Förutom denna sida finns information på kurskatalogen under /info/smalltalk97/labbar/lab3. Se speciellt README-filen. Avsikten med den här laborationen är att du skall få öva på vissa grundläggande delar av UML:s notation, få praktisk övning av några designmönster (eng. design patterns) samt slutligen modellera ett mindre problem med hjälp av UML.

2 Krav

I det här avsnittet beskrivs dom allmänna kraven på den obligatoriska uppgiften och extrauppgiften.

2.1 Designmönsterdelen

I designmönsterdelen av laborationen beskrivs kort ett antal olika designmönster och enkla tillämpningar av dessa. Din uppgift är att antingen implementera en enkel tillämpning med hjälp av givet designmönster, svara på några frågor, alternativt göra en UMLbeskrivining av hur det aktuella mönstret används i en viss situation. Om uppgiften säger att du skall fundera ut, diskutera eller föreslå någon strategi eller mönster skriv då, om det går, ner ett svar på högst två meningar. För mer detaljerad beskrivning se avsnitt 4.

2.2 Brevsystemet

I den här delen skall du konstruera ett enkelt elektroniskt brevsystem. För enkelhets skull tänker vi oss att brevhanteringen i huvudsak sker via AFS-filer.

2.2.1 Kort beskrivning

Se avsnitt 5.

2.2.2 Dokumentation

Du skall konstruera följande: Totalt behöver dina diagram inte ta upp mer än 2-4 sidor.

2.3 Extrauppgiften

I extrauppgiften skall du implementera mailsystemet i VisualWorks och ta hänsyn till några ytterliga krav. Ni skall alltså göra obligatoriska uppgiften och det som beskrivs i avsnitt 6.

2.3.1 OBS!

Ni som vill göra extrauppgiften bör läsa igenom kraven för denna också (avsnitt 6) innan ni börjar designa systemet.

3 Redovisning

Läs först igenom avsnitt 2 och kontrollera att du har uppfyllt kraven. Sedan skall du redovisa det som beskrivs i följande underavsnitt.

3.1 Design patterns

Du skall kunna redogöra för dina svar, implementationer, diagram och kod. Du skall också kunna visa körexempel.

3.2 Brevsystemet

Vi redovisningen skall du ha följande på papper:

3.2.1 Extrauppgiften

Du som har gjort extrauppgiften skall också kunna redogöra för och visa:

4 Åtta designmönster som väntar på att bli utforskade

4.1 Förberedelse

4.1.1 Litteratur

Innan ni börjar med uppgifterna bör ni ha förberett er genom att ha läst igenom erforderliga delar av kursbunten, föreläsningsanteckningar och boken. Speciellt det som handlar om analys, design, UML och designmönster.

Artiklar

Läs följande artiklar ur kursbunten:

4.2 Uppgifter

För att det hela skall bli så enkelt som möjligt att implementera i Smalltalk är vissa beskrivningar lite "Smalltalkfierade". Vidare för att göra lablydelsen kort används inga fullständiga patternbeskrivningar. Den speciellt intresserade kan konsultera patternlitteraturen som rekommenderas på kursens hemsida. Vissa av mönstren har också beskrivits i mer detalj på föreläsningar och övningar. Förutom den för respektive mönster givna uppgiften bör du fundera över när det aktuella mönstret kan vara bra respektive dåligt att använda.

4.2.1 Template Method

Detta mönster är bland annat vanligt förekommande då man jobbar med klasshierarkier och abstrakta klasser. I detta mönster definierar en metod i en superklass ett skelett av en algoritm men detaljer "sparas" till subklasserna.

Figur 1
Figur 1. Exempel med template method

Uppgift

Ange några för respektive nackdelar med detta mönster. Försök också ange några specifika situationer där detta mönster kan användas och/eller har använts av dig.

4.2.2 Factory

I många situationer önskar man abstrahera instansieringen av klasser. Man vill tex dölja speciella detaljer eller att olika konkreta klasser används i olika situationer, kanske beroende av externa faktorer som aktuell plattform eller önskad fönsterhanterare.

Abstract Factory

Avsikten med en abstract factory är att istället för att klienter direkt initierar de klasser dom vill utnyttja så konstrueras objekten genom att man ber ett abstract factory om en instans av ett viss slag, alltså inte exakt vilken konkret klass man önskar. Ett abstract factory själv initieras genom att man binder vissa typer av objekt/klasser till vissa namn. Tex kan man vid ett tillfälle binda det symboliska namnet #window till MotifWindow och vid ett annat tillfälle till Win95Window. En av poängerna är att samma klientkod kan användas även om ingående (refererade) objekt kan variera.

Figur 2
Figur 2. Exempel med symbol->klass associationer

Läs Gamma et al 1993 i kursbunten för mer detaljerad beskrivning av detta mönster och om du har möjlighet och lust även Gamma et al 1995. Notera speciellt kommentaren i slutet. Som exempel använde den riteditor som ni kunde testa era klasser i laboration 2 en abstract factory-teknik för att binda en viss typ av figur till en viss klass. På detta sätt kan samma editor enkelt användas för uppsättningar av figurklasser med olika namn. Endast en ominitiering av namedParts-katalogen behövs.

Factory method

En factory method definierar ett gränssnitt för att skapa objekt, men låter subklasser avgöra vilket objekt som skall initieras.

Figur 3
Figur 3. Factory method

Uppgift

Ni skall göra två olika deluppgifter baserade på factorymönstren.

4.2.3 Prototype

En prototype är ett objekt som definierar en "prototypinstans". Nya objekt bildas genom kopiering av denna instans.

Figur 5
Figur 5. Prototype

Ett (artificiellt) exempel på användande av en prototyp skulle kunna se ut som följer:

   prototypePen := Pen colored: ColorValue blue nibSize:3.
   pen1 := prototypePen clone.
   pen2 := prototypePen clone.
   pen2 nibSize: 4.
   pen1 drawOn: graphicsContext.
   pen2 drawOn: graphicsContext,
Eller mer på riktigt i VisualWorks (se filen /info/kurser/smalltalk97/labbar/lab3/prototypeTest.st):
   | redProto blueProto geo1 geo2 geo3 points rand |
   "Vi konstruerar en pekare till aktuellt fönsters grafiska kontext"

   redProto := ScheduledControllers activeController view graphicsContext.

   "Se klassen GraphicsContext (tex via GraphicsContext browse)
    för information om dom grafiska rutinerna"

   redProto clear.
   redProto paint: ColorValue red.
   redProto lineWidth: 7.
   blueProto := redProto copy.
   blueProto paint: ColorValue royalBlue.
   geo1 := redProto copy.
   geo2 := redProto copy.
   geo3 := blueProto copy.
   geo1 joinStyle: GraphicsContext joinRound.
   points := OrderedCollection new.
   rand := Random new.
   1 to: 10 do: [:i | points add: (i * rand next @ (i next) * 50) truncated].
   geo1 displayPolyline: points at: 10 @ 20.
   geo2 displayPolyline: points at: 30 @ 0.
   geo3 displayPolyline: points at: 40 @ 30.

Uppgift

Prova koden ovan. Kan du se några andra, eller liknande, användningsområden för en prototyp? Ge några förslag!

4.2.4 Singleton

Mönstret singleton ser till att en klass endast har en instans och erbjuder också en global accessväg till den. Tex kan man ha flera utskriftsjobb till en skrivare men vanligen bara en utskriftskö. I Smalltalk är alla metaklasser singleton. Ett annat exempel där mönstret är användbart är för att hantera en viss "service" som hanterar någon viss typ av socketkommunikation.

Figur 6
Figur 6. Singleton

Uppgift

Konstruera en klass Singleton i enlighet med beskrivningen i figur 6. Observera att soleInstance kan deklareras antingen some en klassvariabel eller en instansvariabel på klassidan. Vilka för- respektive nackdelar har det ena respektive andra sättet? TIPS: Se sidorna 107 resp 116 i Smalltalkboken. Testa klassen och försäkra dig om att den fungerar.

Fundera gärna över andra användningsområde för mönstret. Har du några förslag?

4.2.5 Adapter

En adapter är ett objekt som konverterar ett objekts gränssnitt till ett som en klient förväntar sig. Exempel

Figur 7
Figur 7. Adapter klassdiagram

Figur 8
Figur 8. Adapter sammarbetsdiagram

Uppgift

I filen /info/kurser/smalltalk97/labbar/lab3/repository.st hittar du en enkel klass Repository med en instansvariabel subject och acccessmetoderna subject respektive subject:.

Undersök klassen PluggableAdaptor i systemet. Titta också gärna på hur klassen används (hur hittar jag dessa ställen direkt från browsern?). Undersök också gärna dess subklass TypeConverter för tips om hur du kan implementera en adaptor i Smalltalk.

Efter att detta är gjort skall en Human's mailadress kunna ges till ett Repository mha följande kod:

    repositoryAdaptor value: humanAdaptor value
och en mailadress för en viss Human ändras mha:
    humaAdaptor value: 'nyttmail'
    [:m :v | m email: v.
             repository subject: v]

4.2.6 Proxy

En proxy är ett objekt som fungerar som ett surrogat för ett objekt som befinner sig på annan plats. Avsikten är att användare inte skall se någon skillnad på om det "riktiga objektet" används eller om en proxy ersätter det. Exempel

Uppgift

I den här uppgiften skall ni först använda klassen Image för att konstruera och rita en bild på skärmen. Pröva: Image fromUser. Som andra moment skall ni konstruera en proxy som skall göra det möjligt att hantera en bild som befinner sig på en fil som om den bafann sig i minnet. Proxyn behöver för enkelhets skull endast klara av en delmängd av alla meddellanden en Image förstår. För en mer generell implementation kan tex en teknik med doesNotUnderstand: eller liknande utnyttjas (se Smalltalkboken). För enkelhet skull kan du låta proxyn "göra jobbet" att lagra en image på fil. För att minimera antalet filaccesser (och optimera prestandan) kan du också en använda en cache så att en bild som redan finns i minnet inte läses från filen igen.

Din uppgift är alltså att konstruera en proxy som ersätter en bild som är lagrad i en fil.

Tips: Metoderna för att spara respektive läsa in ett objekt från fil kan använda sig av BinaryObjectStorage dvs ett sätt att hantera objekt på binärformat (som inte heller kräver någon kompilering vid inläsandet). Då skulle metoderna för att lagra respektive rita ut imagen kunna se ut något i stil med (där meddelandena isCached respective cache: har uppenbar mening/metoder):

   save
      self isCached ifTrue:
        [| boss |

           [boss := BinaryObjectStorage onNew: self file writeStream.
            boss nextPut: self cache]
           valueNowOrOnUnwindDo: [boss close]]

   displayOn: gc
       "gc en pekare till en grafisk kontext"

       self isCached ifFalse:
       [| boss |
       [boss := BinaryObjectStorage onOld: self file readStream.
       self cache: boss next]
       valueNowOrOnUnwindDo: [boss close]].

       self cache displayOn: gc
Proxyn kan sedan testas med följande kodavsnitt:
   example
      | imP imP2 gc |

      "först skapar vi en proxy med ett givet namn."
       imP := Proxy on: 'test.im'.

      "sen konstrueras en bild genom att användaren markerar
       ett område på skärmen, som vi också sätter
       till proxyns cache."
       imP cache: Image fromUser.

       "Vi sparar på fil"
       imP save.

       "Vi konstruerar en pekare till aktuellt fönsters grafiska omgivning"
       gc := ScheduledControllers activeController view graphicsContext.

       "Skapar en ny proxy mot angiven fil"
       imP2 := Proxy on: 'test.im'.

       "och testar och jämför tider att rita ut först
        då vi också måste läsa in bilden från fil"
       Transcript print: (Time millisecondsToRun: [imP2 displayOn: gc]); cr.

       "och då bilden finns i (den eventuella) cachen."
       Transcript print: (Time millisecondsToRun: [imP2 displayOn: gc]); cr; endEntry.

Kan du se några för- respektive nackdelar med att använda en cache?

4.2.7 Observer

En observer är beskriven i den rekommenderade läsningen nedan. Läs den och lär om mönstret observer som är väldigt viktigt och flitigt använt inte minst i interaktiva grafiska tillämpningar.

Läs

Utdrag ur Gamma et al 1995, mönstret Observer. Kapitel 9 i Smalltalkboken. Utdrag ur Lewis.

Uppgift

Du ska nu konstruera en klass Mbox, en klass Eater och en MboxInspector enligt figur 9. Observera att du måste komplettera klassbeskrivningarna med lämpliga delar från beskrivningarna i litteraturlistan. Kommentar

Figur 9
Figur 9. Mailbox

Skapa en instans av Mbox och gör både en instans av Eater och en av MboxInspector beroende av mailbox-instansen. Där Eater läser och tar bort det sista brevet från Mboxens lista och MboxInspector skriver ut det i Transcriptfönstret. Som ni märker finns det många potentiella problem, tex

Konstruera klasserna ovan och pröva dem genom att lägga till några "brev" till mailboxen.

5 Brevsystemet

I den här delen skall du konstruera ett enkelt elektroniskt brevsystem. För enkelhets skull tänker vi oss att brevhanteringen i huvudsak sker via AFS-filer. Notera att du inte behöver implementera systemet, om du inte gör extrauppgiften, utan endast göra UML-beskrivning.

5.1 Kort beskrivning

Kort beskrivning av hur systemet skall se ut: