CATEGORII DOCUMENTE |
Programare bazata pe evenimente
Evenimente Swing
Programarea dirijata de evenimente ("Event Driven Programming") se refera la scrierea unor programe care reactioneaza la evenimente externe programului (cauzate de operatorul uman care foloseste programul). Prin "eveniment" se intelege aici un eveniment asincron, independent de evolutia programului si al carui moment de producere nu poate fi prevazut la scrierea programului. Evenimente tipice sunt: apasarea unei taste, actionarea unui buton de "mouse", deplasare "mouse" s.a. Notiunea de eveniment a aparut ca o abstractizare a unei intreruperi externe .
Un program controlat prin evenimente nu initiaza momentul introducerii datelor, dar poate reactiona prompt la orice eveniment produs de o actiune a operatorului. Functiile care preiau date nu sunt apelate direct si explicit de alte functii din program, ci sunt apelate ca urmare a producerii unor evenimente.
Structura unui program dirijat prin evenimente difera de structura unui program obisnuit prin existenta functiilor speciale de tratare a evenimentelor ("Event handlers"), care nu sunt apelate direct din program. Intr-un program dirijat de evenimente exista doua tipuri principale de obiecte:
- Obiecte generatoare de evenimente (sursa unui eveniment);
- Obiecte receptoare de evenimente (obiecte ascultator).
Clasele generator sunt in general clase JFC sau AWT si ele creeaza obiecte "eveniment" ca efect al actiunii operatorului pe suprafata componentei respective.
Clasele receptor de evenimente sunt scrise de catre programatorul aplicatiei pentru ca metodele de tratare a evenimentelor observate sunt specifice fiecarei aplicatii. Aceste clase trebuie sa implementeze anumite interfete JFC, deci trebuie sa contina anumite metode cu nume si semnatura impuse de clasele JFC.
In Java un eveniment este un obiect de un tip clasa derivat din clasa EventObject.
Declansarea unui eveniment are ca efect apelarea de catre obiectul generator a unei metode din obiectul ascultator, care primeste ca argument un obiect "eveniment". Tipul obiectului eveniment este determinat de tipul componetei GUI care a generat evenimentul dar si de actiunea operatorului uman. De exemplu, un clic pe butonul de inchidere a unei ferestre JFrame genereaza un alt eveniment decat un clic pe butonul de micsorare a ferestrei.
Evenimentele JFC pot fi clasificate astfel:
- Evenimente asociate fiecarei componente vizuale (buton, camp text, etc.), generate fie prin "mouse", fie din taste (apasare buton, tastare "Enter", s.a.).
- Evenimente asociate dispozitivelor de introducere ( mouse sau tastatura).
La fiecare obiect generator de evenimente se pot "inregistra" (se pot inscrie) mai multe obiecte ascultator interesate de producerea evenimentelor generate. Operatia de inregistrare se face prin apelarea unei metode de forma "addXListener" (din obiectul generator), unde 'X' este numele (tipul) evenimentului si care este totodata si numele unei interfete.
Tratarea evenimentelor Swing
Programatorul unei aplicatii Java cu evenimente trebuie:
- Sa defineasca clasele ascultator, care implementeaza interfete JFC, prin definirea metodei sau metodelor interfetei pentru tratarea evenimentele observate.
- Sa inregistreze obiectele ascultator la obiectele generatoare de evenimente.
Anumite interfete ascultator ("listener") contin o singura metoda, dar alte interfete contin mai multe metode ce corespund unor evenimente diferite asociate aceleeasi componente vizuale. De exemplu, interfata WindowListener contine sapte metode pentru tratarea diferitelor evenimente asociate unei ferestre (deschidere, inchidere, activare, dezactivare, micsorare).
Pentru a trata numai evenimentul "inchidere fereastra" este suficient sa scriem numai metoda "windowClosing". Daca s-ar defini o clasa care sa implementeze aceasta interfata atunci ar trebui sa definim toate cele sapte metode ale interfetei, majoritatea cu definitie nula (fara efect). Pentru a simplifica sarcina programatorului este prevazuta o clasa "adaptor" WindowAdapter care contine definitii cu efect nul pentru toate metodele interfetei, iar programatorul trebuie sa redefineasca doar una sau cateva metode. Metoda "windowClosing" din aceasta clasa nu are nici un efect si trebuie redefinita pentru terminarea aplicatiei la inchiderea ferestrei principale.
Pentru a compila exemplele care urmeaza se vor introduce la inceput urmatoarele instructiuni:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
In exemplul urmator se defineste o clasa separata, care extinde clasa WindowAdapter si redefineste metoda "windowClosing":
class WindExit extends WindowAdapter
}
class FrameExit
}
Putem folosi si o clasa inclusa anonima pentru obiectul ascultator necesar:
public static void main (String args[ ])
});
f.show ();
}
Evenimente de mouse si de tastatura
Interfata KeyListener contine trei metode : "keyPressed", "keyTyped" si "keyReleased", iar clasa KeyAdapter contine definitii nule pentru cele trei metode. In exemplul urmator este tratat numai evenimentul de tasta apasata prin afisarea caracterului corespunzator tastei:
class KeyEvent1
}
public static void main(String args[])
Evenimentele de la tastatura sunt asociate componentei selectate prin mouse. Este posibila si selectarea prin program a componentei pe care se focalizeaza tastatura folosind metoda care exista in orice componenta JFC numita "requestFocus". In exemplul urmator s-a adaugat un buton de stergere a continutului zonei text, a carui apasare are ca efect focalizarea tastaturii pe campul text.
class KeyEvent2
}
// tratare eveniment buton
class ActHandler implements ActionListener
}
. . .
}
Programele anterioare lucreaza corect numai pentru taste cu caractere afisabile, dar ele pot fi extinse pentru a prezenta orice caracter introdus.
Interfata MouseInputListener contine metode apelate la actiuni pe mouse (apasarea sau eliberare buton: "mouseClicked", "mousePressed", "mouseReleased") si metode apelate la deplasare mouse ("mouseMoved","mouseDragged"). MouseInputAdapter ofera o implementare nula pentru toate cele 7 metode. Exemplul urmator arata cum se poate reactiona la evenimentul de "clic" pe mouse prin numararea acestor evenimente si afisarea lor.
class MouseClicks
});
frame.getContentPane().setLayout (new FlowLayout());
frame.getContentPane().add(label);
frame.setVisible(true);
}
}
Evenimente asociate componentelor JFC
Orice componenta vizuala AWT sau JFC poate fi sursa unui eveniment sau chiar a mai multor tipuri de evenimente, cauzate de un clic pe imaginea componentei sau de apasarea unei taste.
Majoritatea componentelor vizuale sunt interactive, in sensul ca dupa ce se afiseaza forma si continutul componentei este posibil un clic cu mouse-ul pe suprafata componentei sau deplasarea unor elemente ale componentei, ceea ce genereaza evenimente. De asemenea editarea textului dintr-un camp text sau dintr-o zona text produce evenimente.
Fiecare componenta genereaza anumite tipuri de evenimente, si pentru fiecare tip de eveniment este prevazuta o metoda de tratare a evenimentului. Metodele de tratare sunt grupate in interfete. Programatorul trebuie sa defineasca clase ce implementeaza aceste interfete cu metode de tratare a evenimentelor. Astfel de clase "adaptor" au rolul de intermediar intre componenta si aplicatie.
Evenimentele generate de componentele JFC pot fi clasificate in:
- Evenimente comune tuturor componentelor JFC:
ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
- Evenimente specifice fiecarui tip de componenta:
ChangeEvent, MenuEvent, ListDataEvent, ListSelectionEvent, DocumentEvent etc.
Evenimentele comune, mostenite de la clasa Component, pot fi descrise astfel:
- ActionEvent : produs de actiunea asupra unei componente prin clic pe mouse.
- ComponentEvent : produs de o modificare in dimensiunea, pozitia sau vizibilitatea componentei.
- FocusEvent: produs de "focalizarea" claviaturii pe o anumita componenta JFC, pentru ca ea sa poata primi intrari de la tastatura.
- KeyEvent : produs de apasarea unei taste si asociat componentei pe care este focalizata tastatura.
- MouseEvent : produs de apasare sau deplasare mouse pe suprafata componentei.
Numele evenimentelor apar in clasele pentru obiecte "eveniment" si in numele unor metode: "add*Listener", "remove*Listener".
Un buton este un obiect Swing de tip JButton care genereaza cateva tipuri de evenimente: ActionEvent (daca s-a actionat asupra butonului), ChangeEvent (daca s-a modificat ceva in starea butonului) s.a. La apasarea unui buton se creeaza un obiect de tip ActionEvent si se apeleaza metoda numita "actionPerformed" (din interfata ActionListener) pentru obiectul sau obiectele inregistrare ca receptori la acel buton (prin apelul metodei "addActionListener"). Tratarea evenimentului inseamna scrierea unei metode cu numele "actionPerformed" care sa produca un anumit efect ca urmare a "apasarii" butonului (prin clic pe suprafata sa).
In general nu se trateaza toate evenimentele ce pot fi generate de o componenta JFC. Desi un buton poate genera peste 5 tipuri de evenimente, in mod uzual se foloseste numai ActionEvent si deci se apeleaza numai metoda "actionPerformed" din obiectele inregistrate ca receptori pentru butonul respectiv. Se poate spune ca se produc efectiv numai evenimentele pentru care s-au definit ascultatori si deci metode de tratare a evenimentelor. Exemplu de tratare a evenimentului de clic pe un buton, prin emiterea unui semnal sonor la fiecare clic:
class Listener implements ActionListener
}
// creare buton si asociere cu ascultator
class Beeper1
}
Pentru a obtine acelasi efect si prin apasarea unei taste asociate butonului (de exemplu combinatia de taste Ctrl-C) este suficient sa adaugam o instructiune:
buton.setMnemonic (KeyEvent.VK_C);
Se observa ca am folosit clasa "Listener" o singura data, pentru a crea un singur obiect. De aceea se practica frecvent definirea unei clase "ascultator" anonime acolo unde este necesara:
button.addActionListener(new ActionListener()
});
Diferenta dintre evenimentul generat de un buton si un eveniment generat de clic pe mouse este ca primul apare numai daca dispozitivul mouse este pozitionat pe aria ocupata de buton, in timp ce al doilea apare indiferent de pozitia mouse pe ecran. In plus, evenimentele generate de componente JFC contin in ele sursa evenimentului si alte informatii specifice fiecarei componente.
Exemplul urmator foloseste componente de tip JCheckBox (casute cu bifare) care genereaza alt tip de evenimente (ItemEvent), ilustreaza un ascultator la mai multe surse de evenimente si utilizarea unei metode "removeXXXListener":
class CheckBox1 extends JFrame ; // litere afisate in casute
public CheckBox1()
Container cp = getContentPane();
cp.setLayout(new GridLayout(0, 1));
for (int i=0;i<3;i++)
cp.add(cb[i]);
cp.add(a);
}
// Ascultator la casute cu bifare.
class CheckBoxListener implements ItemListener
}
}
Evenimente produse de componente text
Pentru preluarea caracterelor introduse intr-un camp text trebuie tratat evenimentul ActionEvent, generat de un obiect JTextField la apasarea tastei 'Enter'. Pana la apasarea tastei "Enter" este posibila si editarea (corectarea) textului introdus.
Exemplu de validare a continutului unui camp text la terminarea introducerii, cu emiterea unui semnal sonor in caz de eroare :
class MFrame extends JFrame
}
public MFrame ()
// verifica daca un sir contine numai cifre
static boolean isNumber (String str)
public static void main ( String av[])
}
Unele aplicatii prefera sa adauge un buton care sa declanseze actiunea de utilizare a textului introdus in campul text, in locul tastei "Enter" sau ca o alternativa posibila.
Selectarea unui text dintr-o lista de optiuni JComboBox produce tot un eveniment de tip ActionEvent. Exemplu:
// ascultator la ComboBox
class CBListener implements ActionListener
public void actionPerformed (ActionEvent ev)
}
class A ; // lista de optiuni
JComboBox cbox = new JComboBox(s);
JTextField txt = new JTextField(10); // ptr afisare rezultat selectie
Container cp = frm.getContentPane();
cp.add (txt,'South'); cp.add (cbox,'Center');
cbox.addActionListener ( new CBListener (txt)); // ascultator la ComboBox
frm.pack(); frm.show();
}
}
Mecanismul de generare a evenimentelor
De cele mai multe ori, in Java, folosim surse de evenimente prefabricate (componente AWT sau JFC) dar uneori trebuie sa scriem clase generatoare de evenimente, folosind alte clase si interfete JDK. Acest lucru este necesar atunci cand trebuie creata o componenta standard Java ("bean"), deoarece evenimentele fac parte din standardul pentru JavaBeans (componentele standard comunica intre ele si prin evenimente, chiar daca nu sunt componente "vizuale").
La o sursa de evenimente se pot conecta mai multi "ascultatori" interesati de aceste evenimente, dupa cum este posibil ca un acelasi obiect sa poata "asculta" evenimente produse de mai multe surse.
O sursa de evenimente trebuie sa contina o lista de receptori ai evenimentelor generate, care era un vector de referinte la obiecte de tipul Object (in JDK 1.3 s-a introdus clasa EventListenerList pentru a manipula mai sigur aceasta lista).
Un eveniment este incapsulat intr-un obiect de un tip subclasa a clasei generale EventObject, definite in pachetul "java.util" astfel:
public class EventObject extends Object implements Serializable
Mecanismul de producere a evenimentelor si de notificare a obiectelor "ascultator" poate fi urmarit in cadrul claselor JFC de tip "model", unde cuvantul "model" are sensul de model logic al datelor care vor fi prezentate vizual. O clasa model contine date si poate genera evenimente la modificarea sau selectarea unor date continute.
Modelul de buton (DefaultButtonModel) este o sursa de evenimente si contine o lista de "ascultatori" sub forma unui obiect de tipul EventListenerList, care este in esenta un vector de referinte la obiecte ce implementeaza interfata EventListener. Inregistrarea unui obiect ascultator la obiectul sursa de evenimente se face prin apelarea metodei "addActionListener".
Un model de buton trebuie sa respecte interfata ButtonModel, redata mai jos intr-o forma mult simplificata (prin eliminarea a cca 70 % dintre metode):
public interface ButtonModel
Sirul numit "ActionCommand" reprezinta inscriptia afisata pe buton si permite identificarea fiecarui buton prin program (este diferit de numele variabilei JButton ).
Secventa urmatoare contine o selectie de fragmente din clasa model de buton, considerate semnificative pentru aceasta discutie:
public class DefaultButtonModel implements ButtonModel, Serializable
if (b) stateMask |= PRESSED;
else stateMask &= ~PRESSED;
// declansare eveniment "buton actionat"
if(!isPressed() && isArmed())
fireStateChanged();
}
// adaugare receptor la evenimente generate de buton
public void addActionListener(ActionListener l)
// eliminare receptor la evenimente buton
public void removeActionListener(ActionListener l)
// notificare receptori despre producere eveniment
protected void fireActionPerformed (ActionEvent e)
// notificare receptori despre schimbarea starii
protected void fireStateChanged()
}
}
}
Apelarea metodei "setPressed" pentru un buton are acelasi efect cu actiunea operatorului de clic pe imaginea butonului, adica declanseaza un eveniment de tip "ActionEvent" si deci apeleaza metodele "actionPerformed" din toate obiectele ascultator inregistrate la butonul respectiv.
Exista un singur obiect eveniment ChangeEvent transmis ascultatorilor la orice clic pe buton, dar cate un nou obiect ActionEvent pentru fiecare clic, deoarece sirul "actionCommand" poate fi modificat prin apelul metodei "setActionCommand".
Producerea unui eveniment ActionEvent se traduce in apelarea metodei "actionPerformed" pentru toate obiectele de tip ActionListener inregistrate.
Lista de ascultatori la diferite evenimente este unica pentru un obiect EventListenerList, dar in lista se memoreaza si tipul (clasa) obiectului ascultator, impreuna cu adresa sa. Intr-o forma mult simplificata, clasa pentru liste de obiecte ascultator arata astfel:
public class EventListenerList ;
else
}
}
O solutie mai simpla dar mai putin performanta pentru clasa EventListenerList poate folosi o colectie sincronizata in locul unui vector intrinsec extins mereu.
Structura programelor dirijate de evenimente
Intr-un program care reactioneaza la evenimente externe trebuie definite clase ascultator pentru aceste evenimente. De multe ori clasele ascultator trebuie sa comunice intre ele, fie direct, fie prin intermediul unei alte clase. In astfel de situatii avem de ales intre mai multe posibilitati de grupare a claselor din program, fiecare cu avantaje si dezavantaje.
Pentru a ilustra variantele posibile vom folosi un exemplu simplu cu doua butoane si un camp text. In campul text se afiseaza un numar intreg (initial zero); primul buton ('+') are ca efect marirea cu 1 a numarului afisat iar al doilea buton ('-') produce scaderea cu 1 a numarului afisat. Desi foarte simplu, acest exemplu arata necesitatea interactiunii dintre componentele vizuale si obiectele "ascultator".
Prima varianta foloseste numai clase de nivel superior ("top-level"): o clasa pentru fereastra principala a aplicatiei si doua clase pentru tratarea evenimentelor de butoane. Obiectele ascultator la butoanele '+' si '-' trebuie sa actioneze asupra unui camp text si deci trebuie sa primeasca o referinta la acest camp text (in constructor).
// ascultator la buton '+'
class B1L implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// ascultator la buton '-'
class B2L implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// Clasa aplicatiei: butoane cu efect asupra unui camp text
class MFrame extends JFrame
// pentru verificare
public static void main (String args[ ])
}
Numarul afisat in campul text ar putea fi util si altor metode si deci ar putea fi memorat intr-o variabila, desi este oricum memorat ca sir de caractere in campul text. Avem o situatie in care trei clase trebuie sa foloseasca in comun o variabila si sa o modifice. O solutie uzuala este ca variabila externa sa fie transmisa la construirea unui obiect care foloseste aceasta variabila (ca parametru pentru constructorul clasei). Daca variabila este de un tip primitiv (int in acest caz), constructorul primeste o copie a valorii sale, iar metodele clasei nu pot modifica valoarea originala. Clasa Integer nu este nici ea de folos doarece nu contine metode pentru modificarea valorii intregi continute de un obiect Integer si este o clasa finala, care nu poate fi extinsa cu noi metode. Se poate defini o clasa "Int" ce contine o variabila de tip int si o metoda pentru modificarea sa :
class Int // constructor
public int getValue () // citire valoare
public void setValue (int n) // modificare valoare
public String toString() // pentru afisare
}
Clasele pentru obiecte ascultator la evenimente sunt aproape la fel:
// receptor la butonul de incrementare
class B1L implements ActionListener
public void actionPerformed (ActionEvent ev)
}
Clasa cu fereastra principala a aplicatiei :
// Doua butoane care comanda un camp text
class MFrame extends JFrame
}
O alta solutie, mai scurta, porneste de la observatia ca metodele de tratare a evenimentelor difera foarte putin intre ele si ca putem defini o singura clasa ascultator pentru ambele butoane:
class BListener implements ActionListener
public void actionPerformed (ActionEvent ev)
}
O varianta cu numar minim de clase este definirea clasei cu fereastra aplicatiei ca ascultator la evenimente, ceea ce elimina clasele separate cu rol de ascultator :
class MFrame extends JFrame implements ActionListener
public void actionPerformed (ActionEvent ev)
Utilizarea de clase incluse
Pentru reducerea numarului de clase de nivel superior si pentru simplificarea comunicarii intre clasele ascultator la evenimente putem defini clasele receptor ca niste clase interioare cu nume, incluse in clasa cu fereastra aplicatiei:
class MFrame extends JFrame
class B1L implements ActionListener
}
class B2L implements ActionListener
}
Definirea de clase incluse anonime reduce si mai mult lungimea programelor, dar ele sunt mai greu de citit si de extins in cazul unor interactiuni mai complexe intre componentele vizuale. O problema poate fi si limitarea la constructori fara argumente pentru clasele anonime. Exemplul anterior rescris cu clase incluse anonime:
class MFrame extends JFrame
});
b2.addActionListener (new ActionListener()
});
c.setLayout (new FlowLayout());
c.add(b1); c.add(b2);
text.setText(' '+n); c.add (text);
}
} // clasa MFrame poate contine si metoda 'main'
Variabilele referinta la componente vizuale "b1"si "b2" sunt definite de obicei in afara functiilor (ca variabile ale clasei), deoarece este probabil ca mai multe metode sa se refere la aceste variabile. Initializarea lor se poate face in afara functiilor (la incarcarea clasei) deoarece se foloseste un singur obiect fereastra.
Pe de alta parte, functia "main" sau alte metode statice nu pot folosi direct variabilele referinta la butoane si la alte componente vizuale, daca aceste variabile nu sunt definite ca variabile statice.
Clase generator si clase receptor de evenimente
Rolul unui buton sau unei alte componente se realizeaza prin metoda "actionPerformed" de tratare a evenimentelor generate de buton, dar care apare intr-o alta clasa, diferita de clasa buton. De aceea, s-a propus definirea de subclase specializate pentru fiecare componenta Swing folosite intr-o aplicatie. O astfel de clasa contine si metoda "actionPerformed".Obiectele acestor clase sunt in acelasi timp sursa evenimentelor dar si ascultatorul care trateaza evenimentele generate. Exemplu:
// Buton '+'
class IncBut extends JButton implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// Buton '-'
class DecBut extends JButton implements ActionListener
// Doua butoane care comanda un camp text
class MFrame extends JFrame
// functia "main"
}
Comunicarea intre obiectele acestor clase specializate se realizeaza fie prin referinte transmise la construirea obiectului (sau ulterior, printr-o metoda a clasei), fie prin includerea claselor respective intr-o clasa anvelopa.
O varianta a acestei solutii este cea in care de defineste o singura metoda "actionListener" ( mai exact, cate o singura metoda pentru fiecare tip de eveniment), care apeleaza o alta metoda din obiectele buton (sau alt fel de obiecte vizuale specializate). Metoda dispecer de tratare evenimente poate fi inclusa in clasa derivata din JFrame. Alegerea metodei "execute" de tratare efectiva a unui tip de eveniment se face prin polimorfism, functie de sursa evenimentului. Exemplu :
// interfata comuna obiectelor vizuale care executa comenzi
interface Command
// Buton de incrementare
class IncBut extends JButton implements Command
public void execute ()
}
// Un buton care comanda un camp text
class MFrame extends JFrame implements ActionListener
public void actionPerformed (ActionEvent ev)
}
Reducerea cuplajului dintre clase
In examinarea unor variante pentru aplicatia anterioara am urmarit reducerea lungimii codului sursa si al numarului de clase din aplicatie, dar aceastea nu reprezinta indicatori de calitate ai unui program cu obiecte si arata o proiectare fara perspectiva extinderii aplicatiei sau reutilizarii unor parti si in alte aplicatii.
Un dezavantaj comun solutiilor anterioare este cuplarea prea stransa intre obiectele "ascultator" la butoane si obiectul camp text unde se afiseaza rezultatul modificarii ca urmare a actionarii unui buton: metoda "actionPerformed" apeleaza direct o metoda dintr-o alta clasa ("setText" din JTextField). Desi programarea este mai simpla, totusi tratarea evenimentului de buton este specifica acestei aplicatii iar clasele ascultator nu pot fi reutilizate si in alte aplicatii.
Intr-o aplicatie cu multe componente vizuale care interactioneaza este mai dificil de urmarit si de modificat comunicarea directa dintre obiecte. De aceea s-a propus o schema de comunicare printr-o clasa intermediar ("mediator"), singura care cunoaste rolul jucat de celelalte clase. Fiecare din obiectele cu rol in aplicatie nu trebuie sa comunice decat cu obiectul mediator, care va transmite comenzile necesare modificarii unor componente de afisare.
Fiecare dintre obiectele care comunica prin mediator trebuie sa se inregistreze la mediator, care va retine cate o referinta la fiecare obiect cu care comunica.
In exemplul cu doua butoane si un camp text, obiectul mediator este informat de actionarea unuia sau altuia dintre butoane si comanda modificarea numarului afisat in campul text. Pentru reducerea lungimii codului sursa inregistrarea obiectului camp text la mediator se face in constructorul clasei "MFrame":
// Buton '+'
class IncBut extends JButton implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// Buton '-'
class DecBut extends JButton implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// clasa mediator
class Mediator
public void init()
public void inc()
public void dec()
}
// Doua butoane care comanda un cimp text
class MFrame extends JFrame
}
Comunicarea dintre obiecte se face aici prin apel direct de metode: obiectele buton apeleaza metodele "inc" si "dec" ale obiectului mediator, iar mediatorul apeleaza metoda "setText" a obiectului JTextField folosit la afisare. Interactiunea dintre un obiect vizual si mediator poate avea loc in ambele sensuri; de exemplu un buton care comanda mediatorului o actiune dar care poate fi dezactivat de mediator.
O varianta de realizare a aplicatiei oarecum asemanatoare foloseste schema de clase 'observat-observator': butoanele nu actioneaza direct asupra campului text, ci sunt obiecte observate. Obiectul observator actioneaza asupra campului text, in functie de obiectul observat (butonul) care l-a notificat de modificarea sa.
Comunicarea prin evenimente si clase "model"
O alternativa la obiectul mediator este un obiect "model", care difera de un mediator prin aceea ca modelul genereaza evenimente in loc sa apeleze direct metode ale obiectelor comandate.
O clasa "model" seamana cu o componenta vizuala prin aceea ca poate genera evenimente, dar difera de o componenta Swing prin aceea ca evenimentele nu au o cauza externa (o actiune a unui operator uman) ci sunt cauzate de mesaje primite de la alte obiecte din program. Inregistrarea unui obiect ascultator la un obiect model se face la fel ca si la o componenta vizuala.
Putem folosi o clasa model existenta sau sa ne definim alte clase model cu o comportare asemanatoare dar care difera prin datele continute si prin tipul evenimentelor. In exemplul nostru clasa model trebuie sa memoreze un singur numar (un obiect) si putem alege intre DefaultListModel (care contine un vector de obiecte Object) sau DefaultSingleSelectionModel (care contine un indice intreg).
Programul urmator foloseste clasa DefaultSingleSelectionModel; numarul continut poate fi citit cu "getSelectedIndex" si modificat cu metoda "setSelectedIndex"; se genereaza eveniment de tip ChangeEvent la modificarea valorii din obiectul model. Pentru scurtarea codului am definit o subclasa a clasei model, cu nume mai scurt:
class SModel extends DefaultSingleSelectionModel
public int getElement ()
}
// Buton '+'
class IncBut extends JButton implements ActionListener
public void actionPerformed (ActionEvent ev)
}
// tratare evenimente generate de model
class TF extends JTextField implements ChangeListener
public void stateChanged (ChangeEvent ev)
}
// Doua butoane care comanda un camp text
class MFrame extends JFrame // main
}
Pentru exemplul anterior putem sa ne definim o clasa model proprie, care sa contina o variabila de tipul general Object si sa genereze evenimente de tip ActionEvent, prin extinderea clasei AbstractListModel, care asigura partea de generare a evenimentelor si de gestiune a listei de ascultatori la evenimente.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 2056
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved