Nada

Föreläsning 3: Swingkomponenter, abstrakta klasser.

Interface och abstrakta klasser

Ett interface är ett gränssnitt mellan en klass som implementerar detta interface och en annan klass som använder det. Ofta är det sedan en tredje klass som kopplar samman ett objekt av vardera klassen.
interface Larvig                            { //Gränssnitt
  public void larva()                       ;}

class Larvare implements Larvig             { //Implementerar gränssnitt.
  public void larva()                       {
    System.out.println("larv, larv...")     ;}}

class Larvcirkus                            { //Använder gränssnitt.
  public void larvning(Larvig person)       { //Metoden vet inte vilken
    System.out.println("Larva dej!")        ; //klass person är.
    person.larva()                          ;}}

class Huvudprogram                          { //i en tredje klass
  public static void main(String[] args)    { //kopplas objekt av
    Larvcirkus cirkus = new Larvcirkus()    ; //vardera klassen ihop 
    Larvig larvare = new Larvare()          ;
    cirkus.larvning(larvare)                ;}}
Fastän man inte kan skapa objekt av ett interface går det alltså att deklarera variabler och parametrar som interface. Objektet cirkus kan skapas innan klassen Larvare ens är påtänkt fastän dess metodparameter person en gång kommer att vara en Larvare.
     _______        __________
    /Larvig \______|Larvcirkus|       ____________
    \_______/      |__________|------|Huvudprogram|
        ^                            |            |
     ___|___                         |            |
    |Larvare|------------------------|____________|
    |_______|
Ett mellanting mellan interface och vanlig klass är abstract class som bara är delvis implementerad. Det går därför inte att skapa objekt av den abstrakta klassen med new utan enda användningssättet är att göra extend och implementera dom saknade metoderna.
abstract class AbstractTrams {  //En abstrakt klass
  public String toString()   {  //kan ha både konkreta
    return "TRAMS"           ;} //(implementerade) och
  abstract void trams()      ;} //abstrakta metoder.
Javabibliotekets AbstractButton har faktiskt inga abstrakta metoder men har gjorts abstrakt för att ingen ska luras att skapa objekt av den - den har nämligen inget utseende. Alla konkreta knappklasser som gör extend på den implementerar ett visst utseende, till exempel radioknappar och checkboxar.

Model-View-Control i swingkomponenter

Varje grafisk swingkomponent har en modell, det vill säga ett objekt som innehåller alla data utöver grafiken och som anropas när så behövs. För att modellklassen ska bli utbytbar samlas anropen i ett interface.
     ______________      ___________
    |AbstractButton|--->/ButtonModel\  Anger man ingen modell använder
    |______________|    \___________/  new JButton() DefaultButtonModel
          ^                   ^
   _______|_______            |
 _|_____     _____|___   _____|____________
|JButton|   |JRadioBtn| |DefaultButtonModel|    
|_______|   |_________| |__________________|
I gränssnittet ButtonModel finns bland annat metoderna addActionListener och getActionCommand. DefaultButtonModel är javakod som implementerar metoderna.

JButton

En knapp låter som en enkel komponent men en swingknapp kan massor med saker. Bland annat förstår den HTML-kod, och det gör att man kan få önskat utseende på knapptexten med bilder och allt. Hjälpbubblor är lätt att lägga in.
JButton knapp = new JButton("<html>Tjo<b>hej!</b></html>",
                    new ImageIcon("frog.gif"));
knapp.setToolTipText("Klicka på grodan!");

JFrame och JApplet

Långt innan Java uppfanns hade datorernas operativsystem fönsterhanterarprogram som ritade fönster för nyttoprogrammen att köra i. Nackdelen med dessa tungviktsfönster är att nyttoprogrammet inte har full kontroll över deras utseende och uppförande, därför är swingkomponenterna lättviktare som inte blandar in fönsterhanteraren. Det yttersta fönstret, rotfönstret, måste dock vara en tungviktare och därför lägger JFrame en osynlig lättviktsbehållare omedelbart innanför rotfönstret. Det är i denna ContentPane, inte i själva JFrame-fönstret, man lägger sina komponenter.
  JFrame ram = new JFrame("Rotfönster")                        ;
  ram.getContentPane().add(new JButton("Tryck om du vågar!"))  ;
  ram.getContentPane().add(new JLabel("<-"),BorderLayout.EAST) ;
  ram.setSize(300,400)                                         ;
  ram.setVisible(true)                                         ;

JList

Grafiska komponenter som låter en välja från en lista finns det några olika av. Den allmännaste är JList som låter en använda valfri modell bara den implementerar gränssnittet ListModel. Man kan också nöja sej med den färdiga klassen DefaultListModel.
     ________      _______
    |JList   |    /ListModel\       Gränssnittet har metoderna
    |        |----|         |       getSize() och getItemAt(k),
    |________|    \_________/       alltså det som JList anropar.
                       ^
                  _____|___________
                 |AbstractListModel| Den abstrakta klassen har 
                 |_________________| bland annat getListeners().
                       ^
                  _____|__________
                 |DefaultListModel|  Ungefär en Vector-klass med
                 |________________|  add- och remove-metoder.
Ett färdigt programexempel kommer i föreläsning 4.

JTree

Ett allmänt träd av den typ som visar ett bokverks indelning i volymer, kapitel, avsnitt, stycken etc eller en filkatalogstruktur kan modelleras i en TreeModel och visas upp av komponenten JTree. Ett exempel finns i laboration 4.
      getModel()      getRoot() 
 ________      _______         ________
|JTree   |--->/TreeModel\---->/TreeNode\       Här finns getParent()     
|________|    \_________/     \________/       och getChildren().
                                  ^
                             _____|_________
                            /MutableTreeNode\  Här finns insert och
                            \_______________/  remove.
                                  ^       
                       ___________|__________  *
                      |DefaultMutableTreeNode|<-----  
                      |                      |______|
                      |______________________| child

Designmönster

Ett designmönster som är till just för programutvecklingsfasen är att låta fuskobjekt ersätta objekt av klasser som inte är programmerade än.

Mock Object

Vi ska utveckla ett program för utbetalning av månadslöner. Persondata ligger i en databas, med hjälp av dess uppgifter beräknas personens lön och utbetalningsanmodan skickas till banken. Objektmodellen tycks vara så här
     ________      _________      ___________
    |Databas |<---|Beräkning|--->|Utbetalning|
    |________|    |_________|    |___________|
Beräkningsobjektet gör anropet person=databas.get() och får en datapost för en person, beräknar lönen, skapar en lönepost och anropar utbetalning.pay(lönepost). Tyvärr kan man inte testa klassen Beräkning förrän både Databas och Utbetalning finns. Men om man inför två interface kan man lätt skriva ihop två klasser som implementerar metoderna fuskigt. Till exempel kanske get-metoden alltid returnerar Kalle Ankas data och pay-metoden bara skriver ut lönen på skärmen.
     ________      _________      ___________
    /Databas \<---|Beräkning|--->/Utbetalning\
    \________/    |_________|    \___________/
     ^      ^                      ^       ^
     |      |                      |       |

 ____|__   _|_____           ______|__    _|_______     
|FuskBas| |RealBas|         |RealBetal|  |FuskBetal|
|_______| |_______|         |_________|  |_________|
Så här börjar koden för beräkningsklassen.
   class Beräkning                                   {
     Databas bas                                     ;
     Utbetalning betal                               ;
     public Beräkning(Databas bas, Utbetalning betal);
       this.bas=bas                                  ;
       this.betal=betal                              ;}
Och så här ser testprogrammet ut.
   class Test                                        {
     Databas bas = new FuskBas()                     ;
     Utbetalning betal = new FuskBetal()             ;
     Beräkning beräkning = new Beräkning(bas, betal) ;
      - - -
     beräkning.start()                               ;
I senare testprogram kan fuskobjekten bytas ut mot äkta vara utan att Beräkning behöver kompileras om.

Relation

Vid objektmodellering hamnar man ofta i situationen att två objekt är associerade men att man inte kan bestämma sej för vilket av objekten som har det andra. Ett typexempel är följande
      __________         ______
     | Teknolog |-------| Kurs |
     |__________|       |______|
Från lärarens synpunkt är det kursobjektet som har flera teknologobjekt, men från studentens synpunkt är det tvärtom. Lösningen är oftast att införa ett relationsobjekt.
      __________        ______________        ______
     | Teknolog |<-----| Registrering |----->| Kurs |
     |__________|      |______________|      |______|
Databaser som byggts med detta designmönster kallas relationsdatabaser.
Sidansvarig: <erikf@nada.kth.se>
Senast ändrad 19 januari 2005
Tekniskt stöd: <webmaster@nada.kth.se>