Programiranje

Pogled opazovalca od znotraj

Nedolgo nazaj mi je sklopka odpovedala, zato sem svoj džip pripeljal do lokalne prodajalne. V prodajalcu nisem poznal nikogar in nihče od njih me ni poznal, zato sem jim dal svojo telefonsko številko, da so me lahko obvestili s predračunom. Ta ureditev je delovala tako dobro, da smo po končanem delu storili isto. Ker se mi je vse skupaj izkazalo, sumim, da servisni oddelek v prodajalni uporablja večino kupcev enak vzorec.

Ta vzorec objavi-naroči, kjer je opazovalec registri z a predmet in nato prejme obvestil, je precej pogosta tako v vsakdanjem življenju kot v virtualnem svetu razvoja programske opreme. Pravzaprav Opazovalec vzorec je, kot je znano, ena od povezav objektno usmerjenega razvoja programske opreme, ker omogoča komuniciranje različnih predmetov. S to zmožnostjo lahko predmete vstavite v ogrodje med izvajanjem, kar omogoča zelo prilagodljivo, razširljivo in ponovno uporabno programsko opremo.

Opomba: Izvorno kodo tega članka lahko prenesete iz virov.

Vzorec opazovalca

V Vzorci oblikovanja, avtorji opisujejo vzorec Observerja tako:

Določite medsebojno odvisnost med predmeti, tako da ko en objekt spremeni stanje, se vse odvisne osebe samodejno obvestijo in posodobijo.

Vzorec opazovalca ima enega subjekta in potencialno veliko opazovalcev. Opazovalci se registrirajo pri osebi, ki opazovalce obvesti o dogodkih. Primer prototipičnega Observerja je grafični uporabniški vmesnik (GUI), ki hkrati prikaže dva pogleda enega samega modela; pogledi se registrirajo z modelom in ko se model spremeni, obvesti poglede, ki se ustrezno posodobijo. Poglejmo, kako deluje.

Opazovalci v akciji

Aplikacija, prikazana na sliki 1, vsebuje en model in dva pogleda. Vrednost modela, ki predstavlja povečavo slike, se spreminja s premikanjem drsnega gumba. Pogledi, znani kot komponente v Swingu, so oznaka, ki prikazuje vrednost modela, in podokno za pomikanje, ki sliko prilagodi v skladu z vrednostjo modela.

Model v aplikaciji je primerek DefaultBoundedRangeModel (), ki sledi omejeni celoštevilčni vrednosti - v tem primeru od 0 do 100—S temi metodami:

  • int getMaximum ()
  • int getMinimum ()
  • int getValue ()
  • boolean getValueIsAdjusting ()
  • int getExtent ()
  • void setMaximum (int)
  • void setMinimum (int)
  • void setValue (int)
  • void setValueIsAdjusting (logična vrednost)
  • void setExtent (int)
  • void setRangeProperties (int vrednost, int obseg, int min, int max, logična prilagoditev)
  • void addChangeListener (ChangeListener)
  • void removeChangeListener (ChangeListener)

Kot kažeta zadnji dve zgoraj navedeni metodi, so primeri DefaultBoundedRangeModel () podporo poslušalcem sprememb. Primer 1 prikazuje, kako aplikacija izkorišča to funkcijo:

Primer 1. Dva opazovalca se odzivata na spremembe modela

uvoz javax.swing. *; uvoz javax.swing.event. *; uvoz java.awt. *; uvoz java.awt.event. *; uvoz java.util. *; test javnega razreda razširja JFrame { private DefaultBoundedRangeModel model = nov DefaultBoundedRangeModel (100,0,0,100); zasebni drsnik JSlider = nov JSlider (model); private JLabel readOut = new JLabel ("100%"); private ImageIcon image = new ImageIcon ("shortcake.jpg"); private ImageView imageView = nov ImageView (slika, model); javni test () {super ("Vzorec zasnove opazovalca"); Vsebnik vsebnikaPane = getContentPane (); JPanel plošča = nov JPanel (); panel.add (nov JLabel ("Nastavi velikost slike:")); panel.add (drsnik); panel.add (readOut); contentPane.add (plošča, BorderLayout.NORTH); contentPane.add (imageView, BorderLayout.CENTER); model.addChangeListener (nov ReadOutSynchronizer ()); } public static void main (String args []) {Test test = new Test (); test.setBounds (100,100,400,350); test.show (); } razred ReadOutSynchronizer izvaja ChangeListener {javna praznina stateChanged(ChangeEvent e) {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}} razred ImageView razširja JScrollPane {zasebna plošča JPanel = nov JPanel (); private Dimension originalSize = new Dimension (); zasebna slika originalImage; zasebna ikona ImageIcon; javni ImageView (ikona ImageIcon, model BoundedRangeModel) {panel.setLayout (nov BorderLayout ()); panel.add (nov JLabel (ikona)); this.icon = ikona; this.originalImage = icon.getImage (); setViewportView (plošča); model.addChangeListener (nov ModelListener ()); originalSize.width = icon.getIconWidth (); originalSize.height = icon.getIconHeight (); } razred ModelListener izvaja ChangeListener {javna praznina stateChanged(ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, value = model.getValue (); dvojni multiplikator = (dvojna) vrednost / (dvojna) razpon; multiplikator = množitelj == 0,0? 0,01: množitelj; Slika pomanjšana = originalImage.getScaledInstance ((int) (množitelj originalSize.width *), (int) (množitelj originalSize.height *), Image.SCALE_FAST); icon.setImage (pomanjšana); panel.revalidate (); panel.repaint (); }}}} 

Ko premaknete drsnik, drsnik spremeni vrednost modela. Ta sprememba sproži obvestila o dogodkih za dva poslušalca sprememb, registrirana z modelom, ki prilagodita odčitek in prilagodita sliko. Oba poslušalca uporabljata dogodek spremembe, ki mu je bil poslan

stateChanged ()

za določitev nove vrednosti modela.

Swing je težek uporabnik vzorca Observer - izvaja več kot 50 poslušalcev dogodkov za izvajanje vedenja, specifičnega za aplikacijo, od odziva na pritisnjen gumb do veta na dogodek zapiranja okna za notranji okvir. Toda Swing ni edini okvir, ki dobro uporablja vzorec Observer - pogosto se uporablja v Java 2 SDK; na primer: Abstract Window Toolkit, ogrodje JavaBeans, javax.naming paketi in vhodno / izhodni upravljavci.

Primer 1 posebej prikazuje uporabo vzorca Observer z Swingom. Preden bomo razpravljali o podrobnostih vzorca Observer, poglejmo, kako se vzorec na splošno izvaja.

Kako deluje vzorec Observer

Slika 2 prikazuje, kako so povezani objekti v vzorcu Observer.

Zadeva, ki je vir dogodka, vzdržuje zbirko opazovalcev in ponuja metode za dodajanje in odstranjevanje opazovalcev iz te zbirke. Predmet izvaja tudi a obvestiti() metoda, ki vsakega registriranega opazovalca obvesti o dogodkih, ki ga opazovalca zanimajo. Subjekti obvestijo opazovalce s sklicevanjem na opazovalce nadgradnja() metoda.

Slika 3 prikazuje diagram zaporedja za vzorec Observer.

Običajno neki nepovezani predmet prikliče metodo subjekta, ki spreminja stanje subjekta. Ko se to zgodi, subjekt prikliče svoje obvestiti() metoda, ki se ponovi nad zbiranjem opazovalcev in pokliče opazovalce nadgradnja() metoda.

Vzorec opazovalca je eden najpomembnejših vzorcev oblikovanja, ker omogoča komuniciranje močno ločenih predmetov. V primeru 1 je model omejenega obsega edino, kar ve o svojih poslušalcih, ta, da izvajajo a stateChanged () metoda. Poslušalce zanima samo vrednost modela, ne pa tudi način izvedbe modela. Model in njegovi poslušalci zelo malo vedo drug o drugem, a zahvaljujoč vzorcu Observer lahko komunicirajo. Ta visoka stopnja ločevanja med modeli in poslušalci vam omogoča izdelavo programske opreme, sestavljene iz vtičnih predmetov, zaradi česar je vaša koda zelo prilagodljiva in ponovno uporabna.

SDK za Java 2 in vzorec Observer

Java 2 SDK ponuja klasično izvedbo vzorca Observer z Opazovalec vmesnik in Opazno razred iz java.util imenik. The Opazno razred predstavlja predmet; opazovalci izvajajo Opazovalec vmesnik. Zanimivo je, da se ta klasična izvedba vzorca Observer v praksi redko uporablja, ker zahteva, da subjekti podaljšajo Opazno razred. Zahteva dedovanja je v tem primeru slaba zasnova, ker je potencialno katera koli vrsta predmeta kandidat za predmet in ker Java ne podpira večkratnega dedovanja; pogosto imajo kandidati za predmet že superrazred.

Izvedba vzorca Observer na podlagi dogodkov, ki je bila uporabljena v prejšnjem primeru, je velika izbira za izvajanje vzorca Observer, ker ne zahteva, da subjekti razširijo določen razred. Namesto tega predmeti sledijo dogovoru, ki zahteva naslednje metode registracije javnega poslušalca:

  • void addXXXListener (XXXListener)
  • void removeXXXListener (XXXListener)

Kadarkoli je predmet vezana lastnina (lastnost, ki so jo opazili poslušalci) se spremeni, zadeva se ponovi nad poslušalci in prikliče metodo, določeno s XXXListener vmesnik.

Do zdaj bi že moral dobro razumeti vzorec opazovalca. Preostanek tega članka se osredotoča na nekatere natančnejše točke opazovalca.

Anonimni notranji razredi

V primeru 1 sem uporabil notranje razrede za izvajanje poslušalcev aplikacije, ker so bili razredi poslušalcev tesno povezani s svojim zaprtim razredom; lahko pa poslušalce izvajate na kakršen koli način. Ena izmed najbolj priljubljenih odločitev za obdelavo dogodkov uporabniškega vmesnika je anonimni notranji razred, to je razred brez imena, ki je ustvarjen v vrstici, kot je prikazano v primeru 2:

Primer 2. Implementirajte opazovalce z anonimnimi notranjimi razredi

... test javnega razreda razširja JFrame {... javni test () {... model.addChangeListener (novo ChangeListener () {public void stateChanged (ChangeEvent e) {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}); } ...} razred ImageView razširja JScrollPane {... javni ImageView (končna ikona ImageIcon, model BoundedRangeModel) {... model.addChangeListener (novo ChangeListener () {public void stateChanged (ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, value = model.getValue (); dvojni multiplikator = (dvojna) vrednost / (dvojna) razpon; multiplikator = množitelj == 0,0? 0,01: množitelj; Slika pomanjšana = originalImage.getScaledInstance ((int) (množitelj originalSize.width *), (int) (množitelj originalSize.height *), Image.SCALE_FAST); icon.setImage (pomanjšana); panel.revalidate (); }}}); }} 

Koda primera 2 je funkcionalno enakovredna kodi primera 1; vendar zgornja koda uporablja anonimne notranje razrede za definiranje razreda in ustvarjanje primerka v enem zamahu.

Obdelovalec dogodkov JavaBeans

Uporaba anonimnih notranjih razredov, kot je prikazano v prejšnjem primeru, je bila zelo priljubljena pri razvijalcih, zato je JavaBeans, od Java 2 Platform, Standard Edition (J2SE) 1.4, prevzela odgovornost za izvajanje in instanciranje teh notranjih razredov z EventHandler razred, kot je prikazano v primeru 3:

Primer 3. Uporaba java.beans.EventHandler

uvoz java.beans.EventHandler; ... test javnega razreda razširja JFrame {... javni test () {... model.addChangeListener (EventHandler.create (ChangeListener.class, to, "updateReadout")); } ... javna praznina updateReadout () {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }} ... 

$config[zx-auto] not found$config[zx-overlay] not found