Programovanie

Pohľad zvnútra na Pozorovateľa

Nie je to tak dávno, čo mi spojka vypadla, a tak som nechal Jeep odtiahnuť k miestnemu predajcovi. V predajni som nikoho nepoznal a nikto z nich ma nepoznal, preto som im dal svoje telefónne číslo, aby ma mohli oznámiť odhadom. Toto usporiadanie fungovalo tak dobre, že sme urobili to isté, keď boli práce dokončené. Pretože to všetko pre mňa dopadlo perfektne, mám podozrenie, že servisné oddelenie v autorizovanom zastúpení používa rovnaký model ako väčšina jeho zákazníkov.

Tento vzor na zverejnenie a prihlásenie na odber, kde pozorovateľ registre s a predmet a následne prijíma oznámenia, je úplne bežné, a to ako v každodennom živote, tak aj vo virtuálnom svete vývoja softvéru. V skutočnosti Pozorovateľ pattern, ako je známe, je jedným zo základných línií objektovo orientovaného vývoja softvéru, pretože umožňuje komunikovať odlišné objekty. Táto schopnosť vám umožňuje za behu modulu pripojiť objekty do rámca, čo umožňuje vysoko flexibilný, rozšíriteľný a opakovane použiteľný softvér.

Poznámka: Zdrojový kód tohto článku si môžete stiahnuť zo zdrojov.

Vzor Pozorovateľ

V Dizajnové vzory, autori opisujú vzor pozorovateľa takto:

Definujte závislosť jedného až mnohých medzi objektmi tak, aby pri zmene stavu jedného objektu boli všetky jeho závislé osoby informované a automaticky aktualizované.

Vzor pozorovateľa má jeden predmet a potenciálne veľa pozorovateľov. Pozorovatelia sa zaregistrujú u subjektu, ktorý ich upozorní, keď dôjde k udalosti. Prototypovým príkladom Observeru je grafické užívateľské rozhranie (GUI), ktoré súčasne zobrazuje dva pohľady na jeden model; zobrazenia sa zaregistrujú s modelom a keď sa model zmení, oznámi to zobrazeniam, ktoré sa podľa toho aktualizujú. Pozrime sa, ako to funguje.

Pozorovatelia v akcii

Aplikácia zobrazená na obrázku 1 obsahuje jeden model a dva pohľady. S hodnotou modelu, ktorá predstavuje zväčšenie obrázka, sa manipuluje posunutím posúvača. Zobrazenia, známe ako komponenty v aplikácii Swing, sú štítkom, ktorý zobrazuje hodnotu modelu, a rolovacím panelom, ktorý upravuje mierku obrázka v súlade s hodnotou modelu.

Model v aplikácii je inštanciou DefaultBoundedRangeModel (), ktorý sleduje ohraničenú celočíselnú hodnotu - v tomto prípade od 0 do 100—S týmito metódami:

  • int getMaximum ()
  • int getMinimum ()
  • int getValue ()
  • boolean getValueIsAdjusting ()
  • int getExtent ()
  • void setMaximum (int)
  • void setMinimum (int)
  • void setValue (int)
  • void setValueIsAdjusting (boolean)
  • void setExtent (int)
  • void setRangeProperties (int hodnota, int rozsah, int min, int max, boolean úprava)
  • void addChangeListener (ChangeListener)
  • void removeChangeListener (ChangeListener)

Ako naznačujú posledné dve metódy uvedené vyššie, prípady DefaultBoundedRangeModel () podpora poslucháčov zmien. Príklad 1 ukazuje, ako aplikácia využíva túto funkciu:

Príklad 1. Dvaja pozorovatelia reagujú na zmeny modelu

importovať javax.swing. *; import javax.swing.event. *; import java.awt. *; import java.awt.event. *; import java.util. *; test verejnej triedy rozširuje JFrame { private DefaultBoundedRangeModel model = nový DefaultBoundedRangeModel (100,0,0,100); súkromný posúvač JSlider = nový JSlider (Model); private JLabel readOut = new JLabel ("100%"); private ImageIcon image = new ImageIcon ("shortcake.jpg"); privátny ImageView imageView = nový ImageView (obrázok, model); public Test () {super ("Dizajnový vzor pozorovateľa"); Kontajner contentPane = getContentPane (); Panel JPanel = nový JPanel (); panel.add (nový JLabel ("Nastaviť veľkosť obrázka:")); panel.add (posúvač); panel.add (readOut); contentPane.add (panel, 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 (); } trieda ReadOutSynchronizer implementuje ChangeListener {public void stateChanged(ChangeEvent e) {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}} trieda ImageView rozširuje JScrollPane {súkromný panel JPanel = nový JPanel (); súkromná dimenzia originalSize = nová dimenzia (); súkromný obrázok originalImage; súkromná ikona ImageIcon; public ImageView (ikona ImageIcon, model BoundedRangeModel) {panel.setLayout (nový BorderLayout ()); panel.add (nový JLabel (ikona)); this.icon = ikona; this.originalImage = icon.getImage (); setViewportView (panel); model.addChangeListener (nový ModelListener ()); originalSize.width = ikona.getIconWidth (); originalSize.height = icon.getIconHeight (); } trieda ModelListener implementuje ChangeListener {public void stateChanged(ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), rozpätie = max - min, hodnota = model.getValue (); dvojitý multiplikátor = (dvojitá) hodnota / (dvojitá) rozpätie; multiplikátor = multiplikátor == 0,0? 0,01: multiplikátor; Obrázok zmenšený = originalImage.getScaledInstance ((int) (originalSize.width * multiplikátor), (int) (originalSize.height * multiplikátor), Image.SCALE_FAST); ikona.setImage (v mierke); panel.revalidate (); panel.repaint (); }}}} 

Keď pohnete posúvačom, posúvač zmení hodnotu svojho modelu. Táto zmena spustí upozornenie na udalosti dvom poslucháčom zmien zaregistrovaným v modeli, ktorí upravia načítanie a zväčšia obraz. Obaja poslucháči používajú udalosť zmeny odovzdanú komu

stateChanged ()

na určenie novej hodnoty modelu.

Swing je veľkým používateľom vzoru Observer - implementuje viac ako 50 poslucháčov udalostí na implementáciu správania špecifického pre danú aplikáciu, od reakcie na stlačené tlačidlo až po vetovanie udalosti zatvorenia okna pre vnútorný rámec. Avšak Swing nie je jediný rámec, ktorý dobre využíva vzor Observer - je široko používaný v Java 2 SDK; napríklad: Abstract Window Toolkit, rámec JavaBeans, javax.pomenovanie balík a manipulátory vstupu / výstupu.

Príklad 1 konkrétne ukazuje použitie vzoru Pozorovateľ s Swingom. Predtým, ako prediskutujeme ďalšie podrobnosti vzoru pozorovateľa, pozrime sa, ako sa tento vzor všeobecne implementuje.

Ako funguje vzor pozorovateľa

Obrázok 2 zobrazuje vzťah medzi objektmi vo vzore Observer.

Subjekt, ktorý je zdrojom udalostí, udržiava zbierku pozorovateľov a poskytuje metódy na pridávanie a odstraňovanie pozorovateľov z tejto zbierky. Predmet tiež implementuje a upozorniť () metóda, ktorá upozorňuje každého registrovaného pozorovateľa na udalosti, ktoré ho zaujímajú. Subjekty upozorňujú pozorovateľov tým, že sa dovolávajú pozorovateľov aktualizácia () metóda.

Obrázok 3 zobrazuje sekvenčný diagram vzoru pozorovateľa.

Typicky bude nejaký nesúvisiaci objekt vyvolávať metódu subjektu, ktorá upravuje stav subjektu. Keď sa to stane, subjekt si privolá svoje upozorniť () metóda, ktorá iteruje nad zbierkou pozorovateľov volajúcich každého pozorovateľa aktualizácia () metóda.

Vzor Pozorovateľ je jedným z najzákladnejších vzorov návrhu, pretože umožňuje komunikáciu vysoko oddelených objektov. V príklade 1 vie model obmedzeného rozsahu o svojich poslucháčoch len to, že implementujú a stateChanged () metóda. Poslucháčov zaujíma iba hodnota modelu, nie to, ako je model implementovaný. Model a jeho poslucháči o sebe vedia veľmi málo, ale vďaka vzoru Observer môžu komunikovať. Tento vysoký stupeň oddelenia medzi modelmi a poslucháčmi vám umožňuje vytvárať softvér zložený z pripojiteľných objektov, vďaka čomu je váš kód vysoko flexibilný a opakovane použiteľný.

Sada Java 2 SDK a vzor Observer

Java 2 SDK poskytuje klasickú implementáciu vzoru Observer s Pozorovateľ rozhranie a Pozorovateľné triedy z java.util adresár. The Pozorovateľné trieda predstavuje predmet; pozorovatelia implementujú Pozorovateľ rozhranie. Je zaujímavé, že táto klasická implementácia modelu pozorovateľa sa v praxi zriedka používa, pretože vyžaduje, aby subjekty rozšírili Pozorovateľné trieda. Vyžadovanie dedičstva je v tomto prípade zlý dizajn, pretože potenciálnym kandidátom na predmet je akýkoľvek typ objektu a pretože Java nepodporuje viacnásobné dedičstvo; často títo predmetní kandidáti už majú nadtriedu.

Implementácia vzoru Observer na základe udalostí, ktorá bola použitá v predchádzajúcom príklade, je ohromnou voľbou pre implementáciu vzoru Observer, pretože nevyžaduje, aby subjekty rozširovali konkrétnu triedu. Namiesto toho sa subjekty riadia konvenciou, ktorá vyžaduje nasledujúce metódy registrácie verejného poslucháča:

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

Kedykoľvek subjekt viazaný majetok (vlastnosť, ktorú poslucháči pozorovali) sa zmení, subjekt iteruje nad svojimi poslucháčmi a vyvolá metódu definovanú XXXListener rozhranie.

Teraz by ste už mali dobre pochopiť vzor Pozorovateľa. Zvyšok tohto článku sa zameriava na niektoré z najlepších bodov pozorovacieho vzoru.

Anonymné vnútorné triedy

V príklade 1 som použil vnútorné triedy na implementáciu poslucháčov aplikácie, pretože triedy poslucháčov boli pevne spojené s ich uzatvárajúcou triedou; poslucháčov však môžete implementovať ľubovoľným spôsobom. Jednou z najpopulárnejších možností spracovania udalostí používateľského rozhrania je anonymná vnútorná trieda, čo je trieda bez názvu, ktorá sa vytvára in-line, ako je to demonštrované v príklade 2:

Príklad 2. Implementujte pozorovateľov s anonymnými vnútornými triedami

... test verejnej triedy rozširuje JFrame {... public Test () {... model.addChangeListener (nový ChangeListener () {public void stateChanged (ChangeEvent e) {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}); } ...} trieda ImageView rozširuje JScrollPane {... verejný ImageView (konečná ikona ImageIcon, model BoundedRangeModel) {... model.addChangeListener (nový ChangeListener () {public void stateChanged (ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel)e.getSource (); if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), rozpätie = max - min, hodnota = model.getValue (); dvojitý multiplikátor = (dvojitá) hodnota / (dvojitá) rozpätie; multiplikátor = multiplikátor == 0,0? 0,01: multiplikátor; Obrázok zmenšený = originalImage.getScaledInstance ((int) (originalSize.width * multiplikátor), (int) (originalSize.height * multiplikátor), Image.SCALE_FAST); ikona.setImage (v mierke); panel.revalidate (); }}}); }} 

Kód z príkladu 2 je funkčne ekvivalentný s kódom z príkladu 1; vyššie uvedený kód však používa anonymné vnútorné triedy na definovanie triedy a vytvorenie inštancie jedným ťahom.

Obsluha udalosti JavaBeans

Používanie anonymných vnútorných tried, ako je uvedené v predchádzajúcom príklade, bolo medzi vývojármi veľmi populárne, takže počnúc platformou Java 2 Platform, Standard Edition (J2SE) 1.4, špecifikácia JavaBeans prevzala zodpovednosť za implementáciu a vytvorenie inštancií týchto vnútorných tried za vás EventHandler triedy, ako je uvedené v príklade 3:

Príklad 3. Používanie java.beans.EventHandler

import java.beans.EventHandler; ... test verejnej triedy rozširuje JFrame {... public Test () {... model.addChangeListener (EventHandler.create (ChangeListener.class, tento, "updateReadout")); } ... verejná neplatnosť updateReadout () {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }} ... 

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