Ohjelmointi

Tarkkailijan sisäkuva

Kauan sitten kytkimeni antoi, joten sain Jeepin hinata paikalliseen jälleenmyyjään. En tuntenut ketään jälleenmyyjässä, eikä kukaan heistä tuntenut minua, joten annoin heille puhelinnumeroni, jotta he voisivat ilmoittaa minulle arvion. Tämä järjestely toimi niin hyvin, että teimme saman, kun työ oli valmis. Koska tämä kaikki sujui minulle täydellisesti, epäilen, että jälleenmyyjän palveluosastolla on sama malli useimpien asiakkaiden kanssa.

Tämä julkaisu-tilausmalli, jossa tarkkailija rekisteröityy a aihe ja myöhemmin vastaanottaa ilmoituksia, on melko yleinen sekä jokapäiväisessä elämässä että ohjelmistokehityksen virtuaalimaailmassa. Itse asiassa Tarkkailija kuvio, kuten tiedetään, on yksi olio-ohjelmistokehityksestä, koska se antaa erilaisten objektien kommunikoida. Tämän kyvyn avulla voit liittää esineitä kehykseen ajon aikana, mikä mahdollistaa erittäin joustavan, laajennettavan ja uudelleenkäytettävän ohjelmiston.

merkintä: Voit ladata tämän artikkelin lähdekoodin Resursseista.

Tarkkailijan kuvio

Sisään Suunnittelumalleja, kirjoittajat kuvaavat Observer-mallia näin:

Määritä objektien välinen riippuvuus yhdestä moniin niin, että kun yksi objekti muuttaa tilaa, kaikista sen riippuvaisista ilmoitetaan ja päivitetään automaattisesti.

Tarkkailijakuviossa on yksi aihe ja mahdollisesti monia tarkkailijoita. Tarkkailijat rekisteröivät aiheen, joka ilmoittaa tarkkailijoille tapahtumien tapahtuessa. Prototyyppinen tarkkailijaesimerkki on graafinen käyttöliittymä (GUI), joka näyttää samanaikaisesti kaksi näkymää yhdestä mallista; näkymät rekisteröivät mallin, ja kun malli muuttuu, se ilmoittaa näkymistä, jotka päivittyvät vastaavasti. Katsotaanpa, miten se toimii.

Tarkkailijat toiminnassa

Kuvassa 1 esitetty sovellus sisältää yhden mallin ja kaksi näkymää. Mallin arvoa, joka edustaa kuvan suurennusta, manipuloidaan siirtämällä liukusäädintä. Näkymät, jotka tunnetaan komponentteina Swingissä, ovat tarra, joka näyttää mallin arvon, ja vierityspaneeli, joka skaalaa kuvan mallin arvon mukaan.

Sovelluksen malli on esimerkki DefaultBoundedRangeModel (), joka seuraa rajattua kokonaislukua - tässä tapauksessa alkaen 0 että 100- näillä menetelmillä:

  • int getMaximum ()
  • int getMinimum ()
  • int getValue ()
  • looginen getValueIsAdjusting ()
  • int getExtent ()
  • void setMaximum (int)
  • void setMinimum (int)
  • void setValue (int)
  • void setValueIsAdjusting (looginen)
  • void setExtent (int)
  • void setRangeProperties (int-arvo, int-laajuus, int min, int max, looginen säätö)
  • void addChangeListener (ChangeListener)
  • void removeChangeListener (ChangeListener)

Kuten kaksi viimeistä edellä lueteltua menetelmää osoittavat, DefaultBoundedRangeModel () tukea muutoksen kuuntelijoita. Esimerkki 1 osoittaa, kuinka sovellus hyödyntää kyseistä ominaisuutta:

Esimerkki 1. Kaksi tarkkailijaa reagoi mallin muutoksiin

tuo javax.swing. *; tuo javax.swing.event. *; tuo java.awt. *; tuo java.awt.event. *; tuo java.util. *; julkisen luokan testi laajentaa JFrame-kehystä { yksityinen DefaultBoundedRangeModel-malli = uusi DefaultBoundedRangeModel (100,0,0,100); yksityinen JSlider-liukusäädin = uusi JSlider (malli-); yksityinen JLabel readOut = uusi JLabel ("100%"); yksityinen ImageIcon-kuva = uusi ImageIcon ("shortcake.jpg"); yksityinen ImageView imageView = uusi ImageView (kuva, malli); julkinen testi () {super ("Tarkkailijan suunnittelumalli"); Säilön sisältöPane = getContentPane (); JPanel-paneeli = uusi JPanel (); panel.add (uusi JLabel ("Aseta kuvan koko:")); panel.add (liukusäädin); panel.add (readOut); contentPane.add (paneeli, BorderLayout.NORTH); contentPane.add (imageView, BorderLayout.CENTER); model.addChangeListener (uusi ReadOutSynchronizer ()); } public staattinen void main (String args []) {Testitesti = uusi testi (); test.setBounds (100,100,400,350); test.show (); } luokka ReadOutSynchronizer toteuttaa ChangeListenerin {julkinen mitätöinti tila muutettu(ChangeEvent e) {Merkkijono s = Kokonaisluku.toString (malli.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}} luokka ImageView laajentaa JScrollPane {private JPanel -paneeli = uusi JPanel (); yksityinen ulottuvuus originalSize = uusi ulottuvuus (); yksityinen Image originalImage; yksityinen ImageIcon-kuvake; public ImageView (ImageIcon-kuvake, BoundedRangeModel-malli) {panel.setLayout (uusi BorderLayout ()); panel.add (uusi JLabel (kuvake)); this.icon = kuvake; this.originalImage = icon.getImage (); setViewportView (paneeli); model.addChangeListener (uusi ModelListener ()); originalSize.width = icon.getIconWidth (); originalSize.height = icon.getIconHeight (); } luokan ModelListener toteuttaa ChangeListener {julkinen mitätöinti tila muutettu(ChangeEvent e) {BoundedRangeModel-malli = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, arvo = model.getValue (); kaksinkertainen kerroin = (kaksinkertainen) arvo / (kaksinkertainen) alue; kerroin = kerroin == 0,0? 0,01: kerroin; Kuva skaalattu = originalImage.getScaledInstance ((int) (originalSize.width * -kerroin), (int) (originalSize.height * -kerroin), Image.SCALE_FAST); icon.setImage (skaalattu); panel.revalidate (); panel.repaint (); }}}} 

Kun liikutat liukusäädintä, liukusäädin muuttaa mallin arvoa. Tämä muutos laukaisee tapahtumailmoitukset kahdelle malliin rekisteröidylle muutoskuuntelijalle, jotka säätävät lukemaa ja skaalaavat kuvaa. Molemmat kuuntelijat käyttävät muutostapahtumaa, jolle välitettiin

stateChanged ()

mallin uuden arvon määrittämiseksi.

Swing on tarkkailijamallin raskas käyttäjä - se ottaa käyttöön yli 50 tapahtumakuuntelijaa sovelluskohtaisen käyttäytymisen toteuttamiseksi, reagoinnista painettuun painikkeeseen ja vetoamalla ikkunan sulkemistapahtuma sisäiselle kehykselle. Mutta Swing ei ole ainoa kehys, joka tuo Observer-mallin hyvään käyttöön - sitä käytetään laajalti Java 2 SDK: ssa; esimerkiksi: Abstract Window Toolkit, JavaBeans-kehys, javax.naming paketti ja syöttö / lähtö käsittelijät.

Esimerkki 1 osoittaa tarkkailijakuvion käytön Swingin kanssa. Ennen kuin keskustelemme lisää tarkkailijan mallin yksityiskohdista, katsotaanpa, kuinka malli yleensä toteutetaan.

Kuinka Observer-malli toimii

Kuva 2 näyttää kuinka tarkkailijakuvion objektit liittyvät toisiinsa.

Kohde, joka on tapahtumalähde, ylläpitää tarkkailijoiden kokoelmaa ja tarjoaa menetelmiä tarkkailijoiden lisäämiseksi ja poistamiseksi kokoelmasta. Kohde toteuttaa myös a ilmoittaa() menetelmä, joka ilmoittaa jokaiselle rekisteröidylle tarkkailijalle tarkkailijaa kiinnostavista tapahtumista. Aiheet ilmoittavat tarkkailijoille kutsumalla tarkkailijan päivittää() menetelmä.

Kuvassa 3 on havainnointikuvion sekvenssikaavio.

Tyypillisesti joku etuyhteydetön objekti vetoaa kohteen menetelmään, joka muuttaa kohteen tilaa. Kun näin tapahtuu, kohde vetoaa omaansa ilmoittaa() menetelmä, joka toistaa tarkkailijoiden kokoelman, kutsuen kutakin tarkkailijaa päivittää() menetelmä.

Observer-malli on yksi perustavanlaatuisimmista suunnittelumalleista, koska se antaa hyvin irrotettujen esineiden kommunikoida. Esimerkissä 1 rajattu kantama malli tietää vain kuuntelijoistaan, että he toteuttavat a stateChanged () menetelmä. Kuuntelijoita kiinnostaa vain mallin arvo, ei malli. Malli ja sen kuuntelijat tietävät hyvin vähän toisistaan, mutta Observer-mallin ansiosta he voivat kommunikoida. Mallien ja kuuntelijoiden välisen suuren erotuksen ansiosta voit rakentaa ohjelmia, jotka koostuvat liitettävistä esineistä, mikä tekee koodistasi erittäin joustavan ja uudelleenkäytettävän.

Java 2 SDK ja Observer-malli

Java 2 SDK tarjoaa Observer-mallin klassisen toteutuksen Tarkkailija käyttöliittymä ja Havaittavissa luokka java.util hakemistoon. Havaittavissa luokka edustaa aihetta; tarkkailijat panevat täytäntöön Tarkkailija käyttöliittymä. Mielenkiintoista on, että tätä klassista Observer-mallin toteutusta käytetään harvoin käytännössä, koska se vaatii aiheita jatkamaan Havaittavissa luokassa. Perinnän vaatiminen on tässä tapauksessa huono muotoilu, koska mahdollisesti minkä tahansa tyyppinen objekti on aihekandidaatti ja koska Java ei tue useita perintöominaisuuksia; usein niillä ehdokkailla on jo superluokka.

Observer-mallin tapahtumapohjainen toteutus, jota käytettiin edellisessä esimerkissä, on valtava valinta Observer-mallin toteuttamiseen, koska se ei vaadi aiheita jatkamaan tiettyä luokkaa. Sen sijaan tutkittavat noudattavat sopimusta, joka edellyttää seuraavia julkisia kuuntelijoiden rekisteröintimenetelmiä:

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

Aina, kun kohde on sidottu omaisuus (ominaisuus, jonka kuulijat ovat havainneet) muuttuu, aihe toistuu kuuntelijoistaan ​​ja käyttää menetelmän, jonka XXXListener käyttöliittymä.

Tähän mennessä sinulla pitäisi olla hyvä käsitys Observer-mallista. Tämän artikkelin loppuosa keskittyy joihinkin Observer-mallin hienompiin kohtiin.

Nimetön sisäiset luokat

Esimerkissä 1 käytin sisäisiä luokkia sovelluksen kuuntelijoiden toteuttamiseen, koska kuuntelijaluokat olivat tiukasti kytketty heidän suljettuun luokkaansa; Kuitenkin voit toteuttaa kuuntelijat haluamallasi tavalla. Yksi suosituimmista vaihtoehdoista käyttöliittymän tapahtumien käsittelemiseksi on nimettömän sisäinen luokka, joka on luokka, jolla ei ole nimeä, joka luodaan linjassa, kuten esimerkki 2 osoittaa:

Esimerkki 2. Toteuta tarkkailijoita, joilla on nimettömiä sisäluokkia

... julkisen luokan testi laajentaa JFrame-kehystä {... julkinen testi () {... model.addChangeListener (uusi ChangeListener () {public void stateChanged (ChangeEvent e) {String s = Kokonaisluku.toString (malli.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}); } ...} luokan ImageView laajentaa JScrollPane {... public ImageView (viimeinen ImageIcon-kuvake, BoundedRangeModel-malli) {... model.addChangeListener (uusi ChangeListener () {public void stateChanged (ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, arvo = model.getValue (); kaksinkertainen kertoja = (kaksinkertainen) arvo / (kaksinkertainen) alue; kerroin = kerroin == 0,0? 0,01: kerroin; Kuva skaalattu = originalImage.getScaledInstance ((int) (originalSize.width * -kerroin), (int) (originalSize.height * -kerroin), Image.SCALE_FAST); icon.setImage (skaalattu); panel.revalidate (); }}}); }} 

Esimerkin 2 koodi on toiminnallisesti samanlainen kuin esimerkin 1 koodi; Yllä oleva koodi käyttää kuitenkin nimettömiä sisäisiä luokkia luokan määrittelemiseen ja ilmentymän luomiseen yhdellä iskulla.

JavaBeans-tapahtumankäsittelijä

Anonyymien sisäisten luokkien käyttö edellisessä esimerkissä esitetyllä tavalla oli kehittäjien keskuudessa erittäin suosittua, joten Java 2 Platform, Standard Edition (J2SE) 1.4: sta alkaen JavaBeans-spesifikaatio on ottanut vastuun näiden sisäisten luokkien toteuttamisesta ja luomisesta puolestasi. Tapahtumakäsittelijä luokka, kuten esimerkissä 3 on esitetty:

Esimerkki 3. java.beans.EventHandler

tuo java.beans.EventHandler; ... julkisen luokan testi laajentaa JFrame-kehystä {... julkinen testi () {... model.addChangeListener (EventHandler.create (ChangeListener.class, tämä, "updateReadout")); } ... julkinen mitätöinti updateReadout () {String s = Kokonaisluku.toString (malli.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }} ... 

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