Användargränssnitt till objektorienterade system
Idéerna på den här sidan bygger huvudsakligen på
en artikelserie ur JavaWorld. Första avsnittet finns här, http://www.javaworld.com/javaworld/jw-07-1999/jw-07-toolbox.html,
därifrån finns länkar till övriga avsnitt.
OOD i allmänhet
Den viktigaste principen inom OOD är inte arv utan delegering.
Det vill säga operationer på vissa data ska delegeras till
det objekt som hanterar datat, hämta inte datat från objektet
för att sedan utföra operationen på något annat ställe.
Om klassdiagrammet ser ut som ett trassligt garnnystan är det definitivt
inte bra, ett objekt ska ha så få beroenden av andra objekt som
det någonsin är möjligt. Däremot ska de metoder ett
objekt tillhandahåller alla ha en klar koppling till det koncept objektet
representerar, och till objektets data. Ett objekt är sina metoder,
vad omärlden anbelangar. Det är endast metoderna som är
åtkomliga utifrån, hur objektets data är representerat
internt i objektet, eller om det över huvud taget är representerat
där, har ingen annan med att göra.
Problemet med OOD av ett användargränssnitt
Så långt är säkert alla överens, nu över
till artikelförfattarens (Allen Holub) åsikter om hur detta tillämpas
på designen av ett användargränssnitt. När användargränssnittet
till ett objekt ska konstrueras uppstår följande problem med
ovanstående principer: Om ingen kod utanför objektet ska vara
beroende av objektets interna konstruktion går det inte att fråga
objektet om dess data och sedan visa dem på skärmen. Gick det
vore vi tvugna att låta den kod som hanterade skärmen både
läsa och skriva objektets interna variabler. För att undvika detta
och låta objektet behålla sitt inre för sig själv måste
det kunna exponera sig själv på skärmen, innehålla
sitt eget användargränssnitt.
Även denna tanke ställer dock till problem. Det finns en mycket
stor risk att kod som har med bildskärmshantering att göra blir
insprängd i en hopplös röra lite överallt i objektets
metoder. Tanken bryter dessutom mot en annan viktig princip, nämligen
att modellen (den logiska representationen av programmets tillstånd
och operationerna på den) ska vara skiljd från användargränssnittet.
En tredje tanke vore att skicka objektet en referens till en frame där
det skulle kunna rita sig, men varje objekt ska knappast ha en egen frame
och hur ska objekt som inte känner till varandra kunna samsas om samma
frame?
Lösningen på problemet: visual proxy
Holubs lösning bygger på mönstret PAC som består
av de tre delarna Presentation, Abstraction och
Controller. Abstraction är i vårt fall själva
objektet vars data ska visas på skärmen. Det är ansvarigt
för att kunna skapa en presentation, till exempel en JComponent,
som innehåller den visuella representationen av objektets data. Det
är denna hankallar visual proxy. Controller kan till
exempel vara en frame. Controllern anropar alla abstrction
som ska visas och begär deras presentation. Sedan sammanställer
den alla presentation i framen och visar den på skärmen.
Nu finns objektets data på skärmen (i dess presentation)
utan att någon utomstående kod känner till någonting
om det och utan att objektet känner till någonting om skärmen!
Inte illa!!
Det som återstår är hur presentation, abstraction
och controller kommunicerar med varandra. Lösningen är som
följer: Alla abstraction, det vill säga alla objekt som
ska visas, implementerar ett gränssnitt. Det kan heta till exempel Showable.
En controller har en förteckning över alla Showable den
ska kunna visa, den behöver dock (och ska inte heller) veta vad det
egentligen handlar om för klasser. Den refererar dem bara som Showable.
Showable innehåller en enda metod, låt oss kalla den
getVisualComponent(), vilken returnerar presentation, till
exempel i form av en JComponent. När en controller ska
visa sina abstraction på skärmen anropar den getVisualComponent()
i allihopa och samlar ihop deras presentation-JComponent:ar
varefter den visar dem på skärmen i sin frame. Alla abstrction
har registrerat sig som händelselyssnare till sina respektive presentation
och får på så sätt reda på om användaren
uppdaterar dess data. Om en abstraction slutligen måste uppdatera
sin presentation medan den syns på skärmen lägger
den in en uppdateringsbegäran på Swings händelsekö med
SwingUtils.invokeLater().
En controller har alltså referenser till samtliga abstraction,
dock endast i form av Showable, och erhåller därifrån
referenser till deras presentation. En abstraction har endast
en referens till sin presentation när den finns på skärmen.
En presentation, slutligen, har inte referens till någon annan.
Klass- och sekvensdiagram för "visual proxy".
Ovanstående är en extremt förenklad variant av den design
som beskrivs i artikelserien. För att till fullo förstå och
kunna utnyttja detta mönster är det förmodligen nödvändigt
att läsa åtminstone de fyra första avsnitten.
Två vanliga frågor ska dock besvaras redan här:
- Hur ska en Presentation kunna visa en meny?
Controller får implementera ett interface (i artiklarna kallas
det MenuSite) som innehåller metoder för att hantera menyer. När
en Presentation ska visas på skärmen kan den hitta den MenuSite
den ligger i med metoden SwingUtilites.getAncestorOfClass(MenuSite.class,
this). Sedan kan den lägga till och ta bort menyer enligt önskan.
För att detta ska fungera måste Presentation veta när den
visas eftersom det är först då getAncestorOfClass()
kan användas. Det kan den få reda på till exempel genom
att lyssna efter ComponentEvent från sig själv.
- Hur ska en Abstraction kunna få en annan att representeras
på skärmen?
Antag till exempel att en klass i modellen (dvs en Abstraction) representerar
en avdelning på ett företag och att en annan representerar en
anställd. Antag vidare att Presentation för avdelningen finns på
skärmen och att användaren utför någon åtgärd
som ska resultera i att information om en av avdelningens anställda visas.
För att åstadkomma detta måste den Abstraction som representerar
avdelningen beordra den som representerar den anställda att rita sig
på skärmen. Så långt är allt väl, men hur
ska denna senare Abstraction kunna rita upp sig om den inte har någon
referens till något fönster på skärmen? Denna fråga
besvaras i uppgift tre av artikelserien men jag förstår faktiskt
inte svaret. Såvitt jag kan se det finns det två alternativ.
Antingen utökas klassdiagrammet ovan så att även Controller
implementerar något interface vilket innehåller en metod som tar
en JPanel och visar den på skärmen. Abstraction måste då
ha en referens till Controller och anropa nyss nämnda metod när
den vill visa sig. Det andra alternativet är att alla Abstraction alltid
har en Presentation hos sin Controller (även om den ibland inte visas
på skärmen). Abstraction för den anställde kan då
visa sig på skärmen genom att rita i sin Presentation utan att
ha någon som helst kunskap om någon Controller.