Programavimas

Laikykitės atsakomybės grandinės

Neseniai iš „Windows“ perėjau į „Mac OS X“ ir džiaugiuosi rezultatais. Bet vėlgi, aš praleidau tik trumpą penkerių metų laiką Windows NT ir XP; prieš tai aš griežtai buvau „Unix“ kūrėjas 15 metų, daugiausia „Sun Microsystems“ mašinose. Man taip pat pasisekė sukurti programinę įrangą naudojant „Nextstep“, vešlų „Unix“ pagrindu veikiantį „Mac OS X“ pirmtaką, todėl esu šiek tiek šališkas.

Be gražios „Aqua“ vartotojo sąsajos, „Mac OS X“ yra „Unix“, be abejo, geriausia egzistuojanti operacinė sistema. „Unix“ turi daug įdomių funkcijų; vienas iš labiausiai žinomų yra vamzdis, kuris leidžia jums sukurti komandų kombinacijas, perduodant vienos komandos išvestį į kitos įvestį. Pvz., Tarkime, kad norite išvardyti šaltinio failus iš šaltinių šaltinio „Struts“, kurie iškviečia arba apibrėžia metodą, pavadintą vykdyti (). Štai vienas būdas tai padaryti naudojant vamzdį:

 grep "execute (" `rasti $ STRUTS_SRC_DIR -name" * .java "" | awk -F: „{print}“ 

grep komanda failuose ieško reguliarių išraiškų; čia aš naudoju jį norėdamas surasti eilutės įvykius vykdyti ( bylose, kurias atkūrė rasti komandą. grepprodukcija išvedama į awk, kuris kiekvienoje eilutėje atspausdina pirmąjį žetoną, kurį žymi dvitaškis grepišvestis (vertikali juosta reiškia vamzdį). Tas žetonas yra failo vardas, todėl galų gale pateikiu failų, kuriuose yra eilutė, sąrašą vykdyti (.

Dabar, kai turiu failų vardų sąrašą, sąrašui rūšiuoti galiu naudoti kitą vamzdį:

 grep "execute (" `rasti $ STRUTS_SRC_DIR -name" * .java "" | awk -F: '{print}' | rūšiuoti

Šį kartą pateikiau failų vardų sąrašą rūšiuoti. Ką daryti, jei norite sužinoti, kiek failų yra eilutė vykdyti (? Su kitu vamzdžiu lengva:

 grep "execute (" "rasti $ STRUTS_SRC_DIR -name" * .java "" | awk -F: '{print}' | rūšiuoti -u | wc -l 

wc komanda skaičiuoja žodžius, eilutes ir baitus. Šiuo atveju aš nurodžiau -l galimybė skaičiuoti eilutes, po vieną eilutę kiekvienam failui. Aš taip pat pridėjau a -u galimybė rūšiuoti užtikrinti kiekvieno failo pavadinimo unikalumą ( -u parinktis filtruoja dublikatus).

Vamzdžiai yra galingi, nes jie leidžia dinamiškai sudaryti operacijų grandinę. Programinės įrangos sistemose dažnai naudojami vamzdžių atitikmenys (pvz., El. Pašto filtrai arba servleto filtrų rinkinys). Vamzdžių ir filtrų centre yra dizaino modelis: atsakomybės grandinė (RK).

Pastaba: Šio straipsnio šaltinio kodą galite atsisiųsti iš išteklių.

RK įvadas

Atsakomybės grandinės modelis naudoja objektų grandinę užklausai tvarkyti, kuri paprastai yra įvykis. Grandinės objektai perduoda užklausą išilgai grandinės, kol vienas iš objektų apdoroja įvykį. Apdorojimas sustabdomas po įvykio.

1 paveiksle pavaizduota, kaip RK modelis apdoroja užklausas.

Į Dizaino modeliai, autoriai apibūdina atsakomybės grandinės modelį taip:

Venkite susieti užklausos siuntėją su jo imtuvu, suteikdami galimybę daugiau nei vienam objektui tvarkyti užklausą. Grandinėkite priėmimo objektus ir perduokite užklausą išilgai grandinės, kol objektas ją apdoros.

Atsakomybės grandinės modelis taikomas, jei:

  • Norite atsieti užklausos siuntėją ir gavėją
  • Keli objektai, nustatyti vykdymo metu, yra kandidatai, kurie tvarko užklausą
  • Nenorite savo kode aiškiai nurodyti tvarkytojų

Jei naudojate RK modelį, nepamirškite:

  • Tik vienas grandinės objektas tvarko užklausą
  • Kai kurios užklausos gali būti netvarkomos

Šie apribojimai, žinoma, yra skirti klasikiniam RK įgyvendinimui. Praktiškai šios taisyklės yra sulenktos; pavyzdžiui, servleto filtrai yra RK įgyvendinimas, leidžiantis keliems filtrams apdoroti HTTP užklausą.

2 paveiksle parodyta RK modelio klasės schema.

Paprastai užklausų tvarkytojai yra bazinės klasės pratęsimai, išlaikantys nuorodą į kitą grandinės tvarkytoją, žinomą kaip įpėdinis. Bazinė klasė gali būti įgyvendinta handRequest () kaip šitas:

 viešoji abstrakčioji klasė „HandlerBase“ ... ... public void handRequest (SomeRequestObject sro) {if (teisių tęsėjas! = null) teisių perėmėjas.handleRequest (sro); }} 

Taigi pagal numatytuosius nustatymus tvarkytojai perduoda užklausą kitam grandinės tvarkytojui. Betono pratęsimas „HandlerBase“ gali atrodyti taip:

 public class SpamFilter išplečia „HandlerBase“ {public void handleRequest (SomeRequestObject mailMessage) {if (isSpam (mailMessage)) {// Jei pranešimas yra šlamštas // atlikite su šlamštu susijusius veiksmus. Nepersiųskite pranešimo. } else {// Pranešimas nėra šlamštas. super.handleRequest (mailMessage); // Perduoti pranešimą kitam grandinės filtrui. }}} 

„SpamFilter“ tvarko užklausą (tikėtina, kad gauna naują el. laišką), jei pranešimas yra šlamštas, todėl užklausa neviršijama; priešingu atveju patikimi pranešimai perduodami kitam tvarkytojui, tikriausiai kitam el. pašto filtrui, norintiems juos išnaikinti. Galų gale paskutinis grandinės filtras gali išsaugoti pranešimą, kai jis praeis per susirinkimą, judėdamas per kelis filtrus.

Atkreipkite dėmesį, kad hipotetiniai aukščiau aptarti el. Pašto filtrai vienas kitą neatmeta: galiausiai tik vienas filtras tvarko užklausą. Galite pasirinkti tai išversti į išorę, leisdami keliems filtrams apdoroti vieną užklausą, o tai yra geresnė „Unix“ vamzdžių analogija. Bet kuriuo atveju pagrindinis variklis yra RK modelis.

Šiame straipsnyje aptarsiu du atsakomybės grandinės diegimo būdus: servleto filtrai, populiarus RK diegimas, leidžiantis keliems filtrams tvarkyti užklausą, ir originalus „Abstract Window Toolkit“ (AWT) įvykių modelis - nepopuliarus klasikinis RK diegimas, kuris galiausiai buvo nebenaudojamas. .

Servletų filtrai

„Java 2 Platform, Enterprise Edition“ (J2EE) ankstyvaisiais laikais kai kurie servletų konteineriai suteikė patogią funkciją, vadinamą servletų grandine, pagal kurią servletui iš esmės buvo galima pritaikyti filtrų sąrašą. „Servlet“ filtrai yra populiarūs, nes jie naudingi saugumui, glaudinimui, registravimui ir kt. Ir, žinoma, galite sudaryti filtrų grandinę, kad atliktumėte kai kuriuos ar visus tuos dalykus, priklausomai nuo vykdymo laiko sąlygų.

Atsiradus „Java Servlet“ specifikacijos 2.3 versijai, filtrai tapo standartiniais komponentais. Skirtingai nuo klasikinio RK, servletų filtrai leidžia keliems grandinės objektams (filtrams) tvarkyti užklausą.

Servleto filtrai yra galingas J2EE priedas. Be to, dizaino modelių požiūriu jie suteikia įdomų posūkį: jei norite pakeisti užklausą ar atsakymą, be RK naudokite ir „Decorator“ modelį. 3 paveiksle parodyta, kaip veikia servletų filtrai.

Paprastas servleto filtras

Norėdami filtruoti servletą, turite atlikti tris veiksmus:

  • Įdiekite servletą
  • Įdiekite filtrą
  • Susiekite filtrą ir servletą

1-3 pavyzdžiai atlieka visus tris veiksmus iš eilės:

1 pavyzdys. Servletas

importuoti java.io.PrintWriter; importuoti javax.servlet. *; importuoti javax.servlet.http. *; public class FilteredServlet pratęsia HttpServlet {public void doGet (HttpServletRequest užklausa, HttpServletResponse atsakymas) išmeta ServletException, java.io.IOException {PrintWriter out = response.getWriter (); out.println ("iškviesta filtruota servletė"); }} 

2 pavyzdys. Filtras

importuoti java.io.PrintWriter; importuoti javax.servlet. *; importuoti javax.servlet.http.HttpServletRequest; viešoji klasė „AuditFilter“ įgyvendina filtrą {private ServletContext app = null; public void init („FilterConfig config“) {app = config.getServletContext (); } public void „doFilter“(„ServletRequest“ užklausa, „ServletResponse“ atsakymas, „FilterChain“ grandinė) išmeta java.io.IOException, javax.servlet.ServletException {app.log ((((HttpServletRequest) užklausa) .getServletPath ()); grandinė.doFiltras(prašymas, atsakymas); } viešasis niekinis sunaikinimas () {}} 

3 pavyzdys. Diegimo aprašas

    auditFilter AuditFilter <filtruoti žemėlapius>auditFilter/ filteredServlet</ filtravimo kartografavimas> filteredServlet FilteredServlet filteredServlet / filteredServlet ... 

Jei prieinate prie servleto su URL / filteredServlet, auditFilter gauna įtrūkimą paprašius prieš servletą. AuditFilter.doFilter rašo į servleto konteinerio žurnalo failą ir skambina chain.doFilter () perduoti prašymą. „Servlet“ filtrų skambinti nereikia chain.doFilter (); jei jie to nepadaro, prašymas nepersiunčiamas. Galiu pridėti daugiau filtrų, kurie būtų iškviečiami tokia tvarka, kokia jie yra deklaruojami ankstesniame XML faile.

Dabar, kai pamatėte paprastą filtrą, pažvelkime į kitą filtrą, modifikuojantį HTTP atsakymą.

Filtruokite atsakymą pagal „Decorator“ modelį

Skirtingai nuo ankstesnio filtro, kai kuriems servleto filtrams reikia modifikuoti HTTP užklausą ar atsakymą. Įdomu tai, kad ši užduotis apima „Decorator“ modelį. „Decorator“ modelį aptariau dviejuose ankstesniuose „Java“ dizaino modeliai straipsniai: „Nustebinkite savo kūrėjo draugus dizaino modeliais“ ir „Papuoškite savo„ Java “kodą“.

4 pavyzdyje pateikiamas filtras, kuris atlieka paprastą paiešką ir pakeitimą atsakymo tekste. Tas filtras papuošia servleto atsaką ir perduoda dekoratorių servletui. Kai servletė baigia rašyti į dekoruotą atsakymą, filtras atlieka paiešką ir pakeitimą atsakymo turinyje.

4 pavyzdys. Paieškos ir pakeitimo filtras

importuoti java.io. *; importuoti javax.servlet. *; importuoti javax.servlet.http. *; viešoji klasė „SearchAndReplaceFilter“ įgyvendina filtrą {privatus „FilterConfig config“; public void init („FilterConfig config“) {this.config = config; } public FilterConfig getFilterConfig () {return config; } public void doFilter („ServletRequest“ užklausa, „ServletResponse“ atsakymas, „FilterChain“ grandinė) meta java.io.IOException, javax.servlet.ServletException {StringWrapper wrapper = naujas StringWrapper((HttpServletResponse) atsakymas); grandinė.doFiltras(prašymas, vyniojamasis popierius); String responseString = pakuotė.įstrigti(); Eilučių paieška = config.getInitParameter ("paieška"); Eilutė pakeisti = config.getInitParameter ("pakeisti"); if (search == null || pakeisti == null) return; // Parametrai netinkamai nustatyti int index = responseString.indexOf (paieška); if (indeksas! = -1) {String beforeReplace = responseString.substring (0, indeksas); String afterReplace = responseString.substring (rodyklė + paieškos.length ()); response.getWriter (). spausdinti(beforeReplace + pakeisti + afterReplace); }} public void sunaikinti () {config = null; }} 

Ankstesnis filtras ieško įvardytų filtro pradinių parametrų Paieška ir pakeisti; jei jie yra apibrėžti, filtras pakeičia pirmąjį Paieška parametro reikšmė su pakeisti parametro reikšmė.

„SearchAndReplaceFilter.doFilter“ () apgaubia (arba papuošia) atsakymo objektą apklotu (dekoratoriumi), kuris stovi atsakyme. Kada „SearchAndReplaceFilter.doFilter“ () skambučių chain.doFilter () Jei norite persiųsti užklausą, jis perduoda pakuotę, o ne pirminį atsakymą. Užklausa persiunčiama servletui, kuris generuoja atsakymą.

Kada chain.doFilter () grįžta, servletas atliekamas su prašymu, todėl einu į darbą. Pirmiausia patikrinu Paieška ir pakeisti filtro parametrai; jei yra, gaunu su atsakymo įvyniojimu susietą eilutę, kuri yra atsakymo turinys. Tada aš padarau pakeitimą ir atspausdinu jį atgal į atsakymą.

5 pavyzdyje pateikiami „StringWrapper“ klasė.

5 pavyzdys. Dekoratorius

importuoti java.io. *; importuoti javax.servlet. *; importuoti javax.servlet.http. *; viešoji klasė „StringWrapper“ išplečia „HttpServletResponseWrapper“ {StringWriter rašytojas = naujas StringWriter (); public StringWrapper (HttpServletResponse response) {super (atsakymas); } public PrintWriter getWriter () {grąžinti naują PrintWriter (rašytojas); } public String toString () {return rašytojas.toString (); }} 

„StringWrapper“, papuošiantis HTTP atsakymą 4 pavyzdyje, yra „HttpServletResponseWrapper“, kuris taupo mus kuriant dekoratorių pagrindinę klasę, skirtą dekoruoti HTTP atsakymus. „HttpServletResponseWrapper“ galiausiai įgyvendina „ServletResponse“ sąsaja, taigi „HttpServletResponseWrapper“ galima perduoti bet kuriam metodui, tikintis a „ServletResponse“ objektas. Štai kodėl „SearchAndReplaceFilter.doFilter“ () gali paskambinti chain.doFilter (užklausa, vyniojamasis popierius) vietoj chain.doFilter (užklausa, atsakymą).

Dabar, kai turime filtrą ir atsakymo įvyniojimą, susiekime filtrą su URL šablonu ir nurodykite paiešką ir pakeiskite šablonus:

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