Nada

Föreläsning 2: Inre klasser, paket, designmönster.

Lyssnare

Tangenttryckningar och musrörelser ger upphov till ett Event-objekt med information om vad som skett. I den första java 1.0 skickades alltid detta meddelande till metoden action, vare sej någon var intresserad av det eller ej. Det blev långsamt och det kunde bli mycket att ta hand om i samma metod. Senare javaversioner skickar eventobjektet till det lyssnarobjekt som installeras med en sats av typen
  knapp.addActionListener(lyssnare)
Här ska lyssnare vara ett objekt med en metod
public void actionPerformed(ActionEvent e)
som anropas vid varje knapptryckning. Närmare bestämt ska lyssnarklassen implementera gränssnittet ActionListener.

Det naturligast kan tyckas vara att skriva en knapplyssnarklass

  class Lyssnare implements ActionListener    {
    public void actionPerformed(ActionEvent e){
      Button knappen = (Button) e.getSource() ;
      knappen.setText("Bra tryck!")           ;}}
och skapa lyssnarobjektet med Lyssnare lyssnare = new Lyssnare(); men det har nackdelen att den utomstående klassen Lyssnare inte kommer ät privata metoder och data i huvudklassen. Den vanligaste lösningen är att låta huvudklassen själv vara lyssnare.
  class Huvud extends Frame implements ActionListener{
    - - -
      Button knapp = new Button("Tryck")             ;
      knapp.addActionListener(this)                  ;
    - - -
    public void actionPerformed(ActionEvent e)       {
      knapp.setText("Bra tryck!")                    ;}}
Det här funkar bra för knappar och textfält eftersom ActionListener bara har en metod, men gränssnittet WindowListener har sju metoder, eftersom fönster kan öppnas, stängas, aktiveras, krympas till ikon osv. Även om bara en av metoder ska användas måste en WindowListener innehålla alla sju. För att förenkla programmerarlivet har java en färdig lyssnarklass WindowAdapter, som implementerar alla sju metoderna men inte gör någonting. Det finns adapterklasser till alla lyssnargränssnitt, faktiskt även till ActionListener. Vi kunde alltså lika gärna ha skrivit så här.
  class Lyssnare extends ActionAdapter        {
    public void actionPerformed(ActionEvent e){
      Button knappen = (Button) e.getSource() ;
      knappen.setText("Bra tryck!")           ;}}
En typisk fönsterlyssnare kan skrivas lika kort med en adapter.
  class FonsterLyssnare extends WindowAdapter {
    public void windowClosing(WindowEvent e)  {
      System.exit(0)                          ;}}
Adaptern kan inte användas för att göra Huvud-klassen till lyssnare, eftersom extend bara kan göras på en klass.

Inre klass

Naturligare är det att lägga lyssnarklassen som en inre klass i huvudklassen, så här:
  class Huvud extends Frame                     {
    class Lyssnare implements ActionListener    {
      public void actionPerformed(ActionEvent e){
      knapp.setText("Bra tryck!")               ;}}
    - - -
    Button knapp = new Button("Tryck")          ;
    knapp.addActionListener(new Lyssnare())     ;
    - - -                                       }
I en inre klass finns alla metoder och variabler i den yttre klassen tillgängliga, men det blir olika objekt och man kan skapa flera objekt av den inre klassen som alla hör ihop med ett och samma yttre objekt. Ett inre objekt kallar sej själv för this och sitt yttre objekt för (i vårt exempel) Huvud.this.

Allra mest slimmad blir koden med en anonym inre klass.

  fonster.addWindowListener(new WindowAdapter(){
    public void windowClosing(WindowEvent e)   {
      System.exit(0)                           ;}});
Efter kompilering syns en inre klass som Huvud$Lyssnare.class och en anonym klass som Huvud$1.class.

Paket

När man överst i javaprogrammet skriver import java.awt.*; känner plötsligt kompilatorn till en massa biblioteksklasser, nämligen alla som ligger i paketet awt. Vill man själv samla några egna klasser i paketet egna gör man så här.
  1. Skriv package egna; allra överst i dessa javafiler och kompilera om dom.
  2. Skapa en underkatalog egna och lägg ner javafilerna och klassfilerna där.
  3. Nu fungerar import egna.* men bara i den katalog som har underkatalogen egna.
  4. I annan katalog måste javac och java få veta sökvägen till katalogen som har underkatalogen egna.
    java -classpath .:/home/teach/henrik/paket Huvud.java
    Om man glömmer .: först i sökvägen letar inte java på aktuell katalog och hittar alltså inte huvudprogrammet!
  5. Alternativt kan man i Unix göra en länk till paketkatalogen
    ln -s ~henrik/paket paket
    Skenbart blir då paket en vanlig underkatalog.

Designmönster

År 1995 gav fyra författare ut Design Patterns som namngav och beskrev tjugotre allmänt användbara klassdiagram. Många av dom förekommer i javas klassbibliotek och det är nyttigt att man har dom klart för sej när man designar egna klasser.

Observer

Det här är det designmönster som lyssnarklasserna följer.
     _______________      _______
    |Observable     |    /Observer\  Subjektet kan göra många
    |---------------|    |--------|  addObserver() och varje   
    |addObserver(..)|    |update()|  lyssnare kan lyssna på
    |notify()       |    |        |  många subjekt. Metoden
    |_______________|    \________/  notify() anropar alla
           ^                 ^       lyssnares update().
           |                 |
           |
         __|____          ___|____
        |Subjekt|*      *|Lyssnare|    
        |_______|--------|________|
Fördelen är den lösa kopplingen mellan subjekt och lyssnare. Det enda subjektet vet om lyssnaren är att den implementerar gränssnittet Observer (runda hörn är svårt att rita!) och lyssnaren behöver inte veta någonting om subjektet. Klassen Observable har en lista över anslutna lyssnare och ser till att dom anropas när subjektet säger notify(). Som parametrar till update kan man skicka med objekt som talar om vad som hänt och var det hänt.

Model-View-Control

Verklighetens objekt kan ofta i programmet med fördel delas upp i tre olika objekt. Ta en klocka som exempel. Den beskrivs logiskt av vissa variabler (visat datum, visad tid, om den går eller ej, hur många timmar till batteriet räcker osv) och det samlar vi i ett objekt som kallas model. Sen har klockan en viss grafisk utformning och det blir ett objekt view. Slutligen kan klockan ställas om, bytas batteri på osv och det blir objektet control. Alla swingkomponenter är uppbyggda efter denna princip, fast control och view slagits samman till ett objekt. När man på skärmen ser en JButton finns alltså även ett objekt som implementerar gränssnittet ButtonModel. Normalt är det ett objekt av klassen DefaultButtonModel men det kan man ändra på om man vill.
Sidansvarig: <henrik@nada.kth.se>
Senast ändrad 26 september 2002
Tekniskt stöd: <webmaster@nada.kth.se>