Programiranje

Sledite verigi odgovornosti

Pred kratkim sem iz operacijskega sistema Windows prešel na Mac OS X in nad rezultati sem navdušen. Ampak spet sem preživel le kratko petletno delo na Windows NT in XP; pred tem sem bil 15 let strogo razvijalec Unixa, večinoma na strojih Sun Microsystems. Imel sem tudi srečo, da sem razvil programsko opremo pod Nextstepom, bujnim predhodnikom Unixa na Mac OS X, zato sem malo pristranski.

Poleg čudovitega uporabniškega vmesnika Aqua je Mac OS X Unix, verjetno najboljši obstoječi operacijski sistem. Unix ima veliko zanimivih funkcij; eden najbolj znanih je cev, ki vam omogoča ustvarjanje kombinacij ukazov tako, da izhod enega ukaza povežete z vhodom drugega. Denimo, da želite navesti izvorne datoteke iz izvorne distribucije Struts, ki prikličejo ali definirajo imenovano metodo izvrši (). Tukaj je en način, kako to narediti s cevjo:

 grep "execute (" `poišči $ STRUTS_SRC_DIR -ime" * .java "` | awk -F: '{print}' 

The grep ukaz išče datoteke v regularnih izrazih; tukaj ga uporabljam za iskanje pojavitev niza izvrši ( v datotekah, ki jih je odkril najti ukaz. grepizhod je v cevi awk, ki v vsako vrstico natisne prvi žeton - ločen s dvopičjem grepizhod (navpična vrstica pomeni cev). Ta žeton je ime datoteke, zato na koncu dobim seznam imen datotek, ki vsebujejo niz izvrši (.

Zdaj, ko imam seznam imen datotek, lahko za razvrščanje seznama uporabim drugo cev:

 grep "execute (" `poišči $ STRUTS_SRC_DIR -ime" * .java "` | awk -F: '{print}' | razvrsti

Tokrat sem sestavil seznam imen datotek razvrsti. Kaj pa, če želite vedeti, koliko datotek vsebuje niz izvrši (? Z drugo cevjo je enostavno:

 grep "execute (" `poišči $ STRUTS_SRC_DIR -ime" * .java "` | awk -F: '{print}' | sort -u | wc -l 

The stranišče ukaz šteje besede, vrstice in bajte. V tem primeru sem navedel -l možnost štetja vrstic, ena vrstica za vsako datoteko. Dodal sem tudi a -u možnost razvrsti za zagotovitev edinstvenosti za vsako ime datoteke ( -u možnost filtrira dvojnike).

Cevi so zmogljive, saj vam omogočajo dinamično sestavljanje verige operacij. Programski sistemi pogosto uporabljajo ekvivalent cevi (npr. E-poštni filtri ali nabor filtrov za strežniški programček). V središču cevi in ​​filtrov je oblikovalski vzorec: Veriga odgovornosti (OR).

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

Uvod OR

Vzorec verige odgovornosti uporablja verigo predmetov za obdelavo zahteve, ki je običajno dogodek. Predmeti v verigi posredujejo zahtevo po verigi, dokler eden od predmetov ne obravnava dogodka. Obdelava se ustavi po obdelavi dogodka.

Slika 1 prikazuje, kako vzorec OR obdeluje zahteve.

V Vzorci oblikovanja, avtorji opisujejo vzorec verige odgovornosti tako:

Izogibajte se povezovanju pošiljatelja zahteve s sprejemnikom, saj da več predmetom priložnost, da obravnavajo zahtevo. Verižite sprejemne predmete in prošnjo pošljite po verigi, dokler je predmet ne obravnava.

Vzorec verige odgovornosti se uporablja, če:

  • Ločiti želite pošiljatelja in prejemnika zahteve
  • Za obdelavo zahteve je več predmetov, določenih med izvajanjem
  • V svoji kodi ne želite izrecno navajati upravljavcev

Če uporabljate vzorec OR, ne pozabite:

  • Samo en predmet v verigi obravnava zahtevo
  • Nekatere zahteve morda ne bodo obdelane

Te omejitve seveda veljajo za klasično izvajanje OR. V praksi se ta pravila upogibajo; na primer, filtri servlet so izvedba CoR, ki več filtrom omogoča obdelavo zahteve HTTP.

Slika 2 prikazuje diagram razreda vzorcev OR.

Običajno so obdelovalci zahtev razširitve osnovnega razreda, ki ohranja referenco na naslednjega obdelovalca v verigi, znan kot naslednik. Osnovni razred se lahko izvaja handleRequest () Všečkaj to:

 javni abstraktni razred HandlerBase {... public void handleRequest (SomeRequestObject sro) {if (naslednik! = null) naslednik.handleRequest (sro); }} 

Torej privzeto upravljavci posredujejo zahtevo naslednjemu obdelovalcu v verigi. Konkreten podaljšek HandlerBase lahko izgleda takole:

 javni razred SpamFilter razširja HandlerBase {public void handleRequest (SomeRequestObject mailMessage) {if (isSpam (mailMessage)) {// Če je sporočilo vsiljena pošta, / ukrepajte v zvezi z neželeno pošto. Ne posreduj sporočila. } else {// Sporočilo ni vsiljena pošta. super.handleRequest (mailMessage); // Pošlji sporočilo naslednjemu filtru v verigi. }}} 

The SpamFilter obravnava zahtevo (verjetno prejem novega e-poštnega sporočila), če je sporočilo neželena, zato zahteva ne gre več; v nasprotnem primeru se zaupanja vredna sporočila posredujejo naslednjemu obdelovalcu, verjetno drugemu e-poštnemu filtru, ki jih želi odstraniti. Sčasoma lahko zadnji filter v verigi shrani sporočilo po prehodu skozi več filtrov.

Upoštevajte, da se zgoraj obravnavani hipotetični e-poštni filtri medsebojno izključujejo: Na koncu samo en filter obravnava zahtevo. Lahko se odločite, da to obrnete navzven, tako da več filtrov obdelate eno samo zahtevo, kar je boljša analogija s cevmi Unix. Kakor koli že, osnovni motor je vzorec OR.

V tem članku obravnavam dve izvedbi vzorcev verige odgovornosti: filtre servlet, priljubljeno izvedbo OR, ki omogoča obdelavo zahteve več filtrom, in izvirni model dogodkov AWT (Abstract Window Toolkit), nepriljubljeno klasično izvajanje OR, ki je bilo na koncu zastarelo .

Filtri za servlet

V zgodnjih dneh Java 2 Platform, Enterprise Edition (J2EE) so nekateri vsebniki strežniških programčkov ponujali priročno funkcijo, imenovano veriženje servletov, pri čemer je bilo mogoče v bistvu uporabiti seznam filtrov. Filtri za strežniške programčke so priljubljeni, ker so uporabni za varnost, stiskanje, beleženje in drugo. In seveda lahko sestavite verigo filtrov, da lahko nekatere ali vse te stvari opravite, odvisno od pogonskih pogojev.

S pojavom Java Servlet Specification različice 2.3 so filtri postali standardne komponente. Za razliko od klasičnega CoR filtri servlet omogočajo več predmetov (filtrov) v verigi, da obravnavajo zahtevo.

Servlet filtri so močan dodatek k J2EE. S stališča oblikovalskih vzorcev ponujajo tudi zanimiv preobrat: če želite spremeniti zahtevo ali odgovor, poleg OR uporabite še vzorec okrasja. Slika 3 prikazuje delovanje filtrov strežniškega programčka.

Preprost filter za strežniški programček

Za filtriranje strežniškega programčka morate narediti tri stvari:

  • Izvedite strežniški programček
  • Uporabite filter
  • Povežite filter in strežniški programček

Primeri 1-3 izvedejo vse tri zaporedne korake:

Primer 1. Servlet

uvoz java.io.PrintWriter; uvoz javax.servlet. *; uvoz javax.servlet.http. *; javni razred FilteredServlet razširja HttpServlet {public void doGet (zahteva HttpServletRequest, odgovor HttpServletResponse) vrže ServletException, java.io.IOException {PrintWriter out = response.getWriter (); out.println ("Priklican filtriran programček"); }} 

Primer 2. Filter

uvoz java.io.PrintWriter; uvoz javax.servlet. *; uvoz javax.servlet.http.HttpServletRequest; javni razred AuditFilter izvaja Filter {private ServletContext app = null; javna void init (FilterConfig config) {app = config.getServletContext (); } javna praznina doFilter(Zahteva za ServletRequest, odgovor ServletResponse, veriga FilterChain) vrže java.io.IOException, javax.servlet.ServletException {app.log ((((HttpServletRequest) zahteva) .getServletPath ()); veriga.doFilter(zahteva, odgovor); } javna void uniči () {}} 

Primer 3. Deskriptor razmestitve

    auditFilter AuditFilter <preslikava filtrov>auditFilter/ filteredServlet</ preslikava filtrov> filteredServlet FilteredServlet filteredServlet / filteredServlet ... 

Če do strežniškega programčka dostopate z URL-jem / filteredServlet, auditFilter dobi razpoko na zahtevo pred strežnikom. AuditFilter.doFilter piše v datoteko dnevnika vsebnika strežniškega programčka in kliče chain.doFilter () za posredovanje zahteve. Za klicanje filtrov strežniškega programčka ni treba chain.doFilter (); v nasprotnem primeru se zahteva ne posreduje. Lahko dodam več filtrov, ki bi jih priklicali v vrstnem redu, kot so navedeni v prejšnji datoteki XML.

Zdaj, ko ste videli preprost filter, si oglejmo še en filter, ki spreminja odziv HTTP.

Odziv filtrirajte z vzorcem okrasja

V nasprotju s prejšnjim filtrom morajo nekateri filtri programčkov spremeniti zahtevo ali odgovor HTTP. Zanimivo je, da ta naloga vključuje vzorec dekoraterja. O vzorcu okrasja sem razpravljal v prejšnjih dveh Vzorci Java oblikovanja članka: "Presenetite svoje prijatelje razvijalce z vzorci oblikovanja" in "Okrasite svojo kodo Java."

Primer 4 navaja filter, ki v telesu odziva izvede preprosto iskanje in zamenjavo. Ta filter okrasi odziv servleta in ga posreduje servletu. Ko strežniški programček konča pisanje v okrašeni odziv, filter izvede iskanje in zamenjavo v vsebini odziva.

Primer 4. Filter za iskanje in zamenjavo

uvoz java.io. *; uvoz javax.servlet. *; uvoz javax.servlet.http. *; javni razred SearchAndReplaceFilter izvaja Filter {private FilterConfig config; javna void init (FilterConfig config) {this.config = config; } javni FilterConfig getFilterConfig () {return config; } public void doFilter (zahteva za ServletRequest, odgovor ServletResponse, veriga FilterChain) vrže java.io.IOException, javax.servlet.ServletException {StringWrapper ovoj = nov StringWrapper((HttpServletResponse) odgovor); veriga.doFilter(prošnja, ovoj); String responseString = wrapper.toString(); String search = config.getInitParameter ("iskanje"); String replace = config.getInitParameter ("replace"); if (search == null || replace == null) return; // Parametri niso pravilno nastavljeni int index = responseString.indexOf (iskanje); if (index! = -1) {String beforeReplace = responseString.substring (0, indeks); String afterReplace = responseString.substring (index + search.length ()); response.getWriter (). print(beforeReplace + replace + afterReplace); }} javna void uniči () {config = null; }} 

Prejšnji filter išče imenovane parametre init filtra Iskanje in zamenjati; če so definirani, filter nadomesti prvo pojavitev Iskanje vrednost parametra z zamenjati vrednost parametra.

SearchAndReplaceFilter.doFilter () zavije (ali okrasi) odzivni predmet z ovojem (okrasnikom), ki je namesto odgovora. Kdaj SearchAndReplaceFilter.doFilter () klici chain.doFilter () za posredovanje zahteve posreduje ovoj namesto prvotnega odgovora. Zahteva se posreduje strežniku, ki ustvari odgovor.

Kdaj chain.doFilter () vrne se strežniški programček opravi z zahtevo, zato grem v službo. Najprej preverim Iskanje in zamenjati parametri filtra; če je prisoten, dobim niz, povezan z ovitkom odziva, to je vsebina odziva. Nato naredim zamenjavo in jo natisnem nazaj v odgovor.

Primer 5 navaja StringWrapper razred.

Primer 5. Dekorater

uvoz java.io. *; uvoz javax.servlet. *; uvoz javax.servlet.http. *; javni razred StringWrapper razširja HttpServletResponseWrapper {StringWriter Writer = new StringWriter (); javni StringWrapper (HttpServletResponse odgovor) {super (odgovor); } public PrintWriter getWriter () {vrni novo PrintWriter (zapisovalnik); } javni String toString () {return writer.toString (); }} 

StringWrapper, ki okrasi odziv HTTP v primeru 4, je razširitev HttpServletResponseWrapper, ki nam prizanaša z napornim ustvarjanjem osnovnega razreda dekoraterja za okrasitev odzivov HTTP. HttpServletResponseWrapper na koncu izvaja ServletResponse vmesnik, torej primeri HttpServletResponseWrapper se lahko prenese na katero koli metodo, ki pričakuje a ServletResponse predmet. Zato SearchAndReplaceFilter.doFilter () lahko pokličete chain.doFilter (zahteva, ovoj) namesto chain.doFilter (zahteva, odziv).

Zdaj, ko imamo filter in ovoj za odzive, povežemo filter z vzorcem URL-ja in določimo vzorce iskanja in zamenjave:

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