Programavimas

„Log4j“ ortogonalumas pavyzdžiu

Ortogonalumas yra sąvoka, dažnai naudojama aprašant modulinę ir prižiūrimą programinę įrangą, tačiau ją lengviau suprasti atlikus atvejo tyrimą. Šiame straipsnyje Jensas Dietrichas demistifikuoja ortogonalumą ir kai kuriuos susijusius dizaino principus, demonstruodamas jų naudojimą populiarioje „Log4j“ paslaugų bibliotekoje. Jis taip pat aptaria, kaip „Log4j“ pora atvejų pažeidžia ortogonalumą, ir aptaria galimus iškeltų problemų sprendimo būdus.

Ortogonalumo samprata remiasi graikišku žodžiu ortogōnios, reiškiantis „stačiakampis“. Jis dažnai naudojamas išreikšti nepriklausomybę tarp skirtingų dimensijų. Kai objektas juda išilgai x-ašis trimatėje erdvėje, jos y ir z koordinatės nesikeičia. Vienos dimensijos pokyčiai nesukelia pokyčių kitoje dimensijoje, o tai reiškia, kad viena dimensija negali sukelti šalutinių poveikių kitiems.

Tai paaiškina, kodėl ortogonalumo sąvoka dažnai naudojama apibūdinant modulinį ir prižiūrimą programinės įrangos projektavimą: mąstymas apie sistemas kaip taškus daugialypėje erdvėje (sukurtas nepriklausomų, stačių kampų) padeda programinės įrangos kūrėjams užtikrinti, kad mūsų pokyčiai vienu sistemos aspektu neturės šalutinio poveikio kitam.

Būna, kad populiarus „Java“ atvirojo kodo registravimo paketas „Log4j“ yra puikus ortogonalumu pagrįsto modulinio dizaino pavyzdys.

„Log4j“ matmenys

Prisijungimas yra tik geresnė versija System.out.println () sakinį, o „Log4j“ yra naudingųjų programų paketas, kuris apibendrina prisijungimo prie „Java“ platformos mechaniką. Be kitų dalykų, „Log4j“ funkcijos leidžia kūrėjams atlikti šiuos veiksmus:

  • Prisijunkite prie skirtingų priedų (ne tik konsolės, bet ir failų, tinklo vietų, reliacinių duomenų bazių, operacinės sistemos žurnalo paslaugų ir dar daugiau)
  • Prisijungti keliais lygiais (pvz., KLAIDA, ĮSPĖJIMAS, INFO ir KLAIDA)
  • Centralizuotai valdykite, kiek informacijos užregistruojama tam tikru registravimo lygiu
  • Norėdami apibrėžti, kaip registravimo įvykis perteikiamas eilutėje, naudokite skirtingus išdėstymus

Nors „Log4j“ turi kitų funkcijų, aš sutelksiu dėmesį į šias tris jo funkcionalumo dimensijas, kad galėčiau ištirti ortogonalumo sampratą ir pranašumus. Atkreipkite dėmesį, kad mano diskusija pagrįsta „Log4j“ versija 1.2.17.

„Log4j“ sistemoje „JavaWorld“

Gaukite „Log4j“ apžvalga ir išmokti rašyti savo pasirinktiniai „Log4j“ priedai. Norite daugiau „Java“ vadovėlių? Gauti „Enterprise Java“ naujienlaiškis pristatomi į jūsų pašto dėžutę.

Atsižvelgiant į „Log4j“ tipus

Priedai, lygis ir išdėstymas yra trys „Log4j“ aspektai, kuriuos galima vertinti kaip nepriklausomus matmenis. Vartoju terminą aspektas čia kaip sinonimas susirūpinimą, reiškiantis dalį programos susidomėjimo ar dėmesio. Šiuo atveju lengva apibrėžti šias tris problemas pagal klausimus, į kuriuos kiekviena kreipiasi:

  • Pareiškėjas: Kur turėtų būti siunčiami žurnalo įvykių duomenys rodyti ar saugoti?
  • Išdėstymas: Kaip turėtų būti pateiktas žurnalo įvykis?
  • Lygis: Kurie žurnalo įvykiai turėtų būti apdorojami?

Dabar pabandykite kartu atsižvelgti į šiuos aspektus trimatėje erdvėje. Kiekvienas taškas šioje erdvėje atspindi galiojančią sistemos konfigūraciją, kaip parodyta 1 paveiksle. (Atkreipkite dėmesį, kad siūlau šiek tiek supaprastintą „Log4j“ vaizdą: kiekvienas 1 paveiksle esantis taškas iš tikrųjų nėra visuotinė visos sistemos konfigūracija, bet vieno Patys medkirčiai gali būti laikomi ketvirtuoju matmeniu.)

1 sąrašas yra tipinis kodo fragmentas, įgyvendinantis „Log4j“:

Sąrašas 1. „Log4j“ diegimo pavyzdys

// sąrankos registravimas! „Logger logger“ = „Logger.getLogger“ („Foo“); Appender appender = naujas ConsoleAppender (); Maketo išdėstymas = new org.apache.log4j.TTCCLayout () appender.setLayout (layout); logger.addAppender (priedas); logger.setLevel (Level.INFO); // pradėkite registruoti! logger.warn („Labas pasaulis“);

Norėčiau, kad pastebėtumėte apie šį kodą, kad jis yra stačias: galite pakeisti priedą, išdėstymą ar lygio aspektą, nepažeisdami kodo, kuris liktų visiškai funkcionalus. Pagal stačiakampį dizainą kiekvienas taškas nurodytoje programos erdvėje yra galiojanti sistemos konfigūracija. Jokie suvaržymai neleidžia apriboti, kurie galimų konfigūracijų erdvės taškai galioja, ar ne.

Ortogonalumas yra galinga sąvoka, nes ji leidžia mums sukurti gana paprastą mentalinį modelį sudėtingiems taikymo atvejams. Ypač galime sutelkti dėmesį į vieną aspektą, nepaisydami kitų aspektų.

Testavimas yra įprastas ir pažįstamas scenarijus, kai ortogonalumas yra naudingas. Mes galime patikrinti žurnalo lygių funkcionalumą, naudodami tinkamą fiksuotą priedo ir maketo porą. Ortogonalumas garantuoja, kad nebus jokių netikėtumų: žurnalo lygiai veiks taip pat, kaip ir bet kuris priedo ir išdėstymo derinys. Tai ne tik patogu (yra mažiau darbo), bet ir būtina, nes būtų neįmanoma patikrinti žurnalo lygių su kiekvienu žinomu priedo ir išdėstymo deriniu. Tai ypač pasakytina apie tai, kad „Log4j“, kaip ir daugelis programinės įrangos įrankių ir komunalinių paslaugų, yra skirti išplėsti trečiosioms šalims.

Ortogonalumo programinės įrangos sudėtingumas sumažėja panašiai kaip matmenys naudojami geometrijoje, kai sudėtingas taškų judėjimas n matmenų erdvėje suskaidomas į palyginti paprastą manipuliavimą vektoriais. Šia galinga idėja yra pagrįstas visas tiesinės algebros laukas.

Ortogonalumo projektavimas ir kodavimas

Jei dabar galvojate, kaip sukurti ir koduoti ortogonalumą savo programose, tada esate tinkamoje vietoje. Pagrindinė idėja yra naudoti abstrakcija. Kiekviena stačiakampio sistemos dimensija yra susijusi su vienu konkrečiu programos aspektu. Tokį matmenį paprastai vaizduos a tipo (klasė, sąsaja ar sąrašas). Dažniausias sprendimas yra naudoti abstraktus tipas (sąsaja arba abstrakti klasė). Kiekvienas iš šių tipų reiškia dimensiją, o tipo egzempliorius - taškus duotame dimensijoje. Kadangi abstrakčių tipų tiesiogiai tiesiogiai nustatyti negalima, reikalingos ir konkrečios klasės.

Kai kuriais atvejais galime apsieiti ir be jų. Pavyzdžiui, mums nereikia konkrečių klasių, kai tipas yra tik žymėjimas ir neapima elgesio. Tada mes galime tiesiog išaiškinti patį matmenį atspindintį tipą ir dažnai iš anksto apibrėžti fiksuotą egzempliorių rinkinį, naudodami statinius kintamuosius arba naudodami aiškų surašymo tipą. 1 sąraše ši taisyklė bus taikoma „lygio“ aspektui.

3 paveikslas. Viduje lygio lygis

Bendra ortogonalumo taisyklė yra vengti nuorodų į konkrečius konkrečius tipus, atspindinčius kitus programos aspektus (aspektus). Tai leidžia jums parašyti bendrą kodą, kuris veiks vienodai visais įmanomais atvejais. Toks kodas vis tiek gali nurodyti egzempliorių savybes, jei jos yra dimensiją apibrėžiančio tipo sąsajos dalis.

Pavyzdžiui, „Log4j“ abstraktus tipas Išdėstymas apibrėžia metodą ignoruojaMetama (). Šis metodas pateikia loginę reikšmę, nurodančią, ar išdėstymas gali pateikti išimties kamino pėdsakus, ar ne. Kai pareiškėjas naudoja maketą, būtų puiku parašyti sąlyginį kodą ignoruojaMetama (). Pvz., Failų teikėjas galėtų atspausdinti išimties kamino pėdsakus System.err naudojant maketą, kuris negalėjo apdoroti išimčių.

Panašiu būdu, a Išdėstymas įgyvendinimas galėtų būti susijęs su konkrečia Lygis pateikdami registravimo įvykius. Pavyzdžiui, jei žurnalo lygis buvo Lygis. KLAIDA, HTML išdėstymo diegimas gali apvynioti žurnalo pranešimą žymose, kurios paverčiamos raudonai. Vėlgi, esmė tame Lygis. KLAIDA apibrėžiamas Lygis, tipas, atspindintis matmenį.

Tačiau turėtumėte vengti nuorodų į konkrečias kitų aspektų įgyvendinimo klases. Jei pareiškėjas naudoja maketą, tai nereikia žinoti kokios rūšies maketo tai yra. 4 paveiksle pavaizduotos geros ir blogos nuorodos.

Keli modeliai ir sistemos leidžia lengviau išvengti priklausomybės nuo įgyvendinimo tipų, įskaitant priklausomybės įvedimą ir paslaugų lokatoriaus modelį.

Ortogonalumo pažeidimas

Apskritai „Log4j“ yra geras ortogonalumo naudojimo pavyzdys. Tačiau kai kurie „Log4j“ kodai pažeidžia šį principą.

„Log4j“ yra priedas, vadinamas JDBCAppender, kuris naudojamas prisijungiant prie reliacinės duomenų bazės. Atsižvelgiant į reliacinės duomenų bazės mastelį ir populiarumą ir į tai, kad dėl to žurnalo įvykiuose galima lengvai ieškoti (naudojant SQL užklausas), JDBCAppender yra svarbus naudojimo atvejis.

JDBCAppender yra skirta prisijungimo prie reliacinės duomenų bazės problemai spręsti paverčiant žurnalo įvykius į SQL ĮDĖTI pareiškimus. Jis išsprendžia šią problemą naudodamas a „PatternLayout“.

„PatternLayout“ naudoja šablonus, kad vartotojui suteiktų maksimalų lankstumą sukonfigūruoti eilutes, sukurtas iš žurnalo įvykių. Šablonas apibrėžiamas kaip eilutė, o šablone naudojami kintamieji yra tiesinami iš žurnalo įvykių vykdymo metu, kaip parodyta 2 sąraše.

Sąrašas 2. PatternLayout

Styginių šablonas = "% p [@% d {dd MMM yyyy HH: mm: ss}% t]% m% n"; Maketo išdėstymas = naujas org.apache.log4j.PatternLayout (modelis); appender.setLayout (maketas);

JDBCAppender naudoja a „PatternLayout“ su schema, apibrėžiančia SQL ĮDĖTI pareiškimas. Visų pirma, norint nustatyti naudojamą SQL sakinį, galima naudoti šį kodą:

Sąrašas 3. SQL intarpo sakinys

public void setSql (String s) {sqlStatement = s; if (getLayout () == null) {this.setLayout (naujas PatternLayout (s)); } else {((PatternLayout) getLayout ()). setConversionPattern (s); }}

Į šį kodą įtraukta numanoma prielaida, kad maketas, jei jis nustatytas prieš naudojant „setLayout“ („Maketas“) apibrėžtas metodas Pareiškėjas, iš tikrųjų yra „PatternLayout“. Kalbant apie ortogonalumą, tai reiškia, kad staiga daugybė taškų 3D kubelyje, kurie naudojami JDBCAppender su išdėstymais, išskyrus „PatternLayout“ nebeatstovaus galiojančių sistemos konfigūracijų! Tai yra, bet kokie bandymai nustatyti SQL eilutę kitu išdėstymu sukeltų vykdymo laiko (klasės perdavimas) išimtį.

5 pav. „JDBCAppender“, pažeidžiantis ortogonalumą

Yra dar viena priežastis JDBCAppenderdizainas yra abejotinas. JDBC turi savo šablonų variklio parengtus pareiškimus. Naudojant „PatternLayout“, tačiau šablonų variklis apeinamas. Tai gaila, nes JDBC iš anksto rengia parengtus pareiškimus, dėl kurių žymiai pagerėja našumas. Deja, tai nėra lengva išspręsti. Akivaizdus būdas būtų kontroliuoti, kokį maketą galima naudoti JDBCAppender pakeisdamas seterį taip.

Sąrašas 4. Nepaisomas setLayout ()

public void setLayout (Layout layout) {if (išdėstymas instanceOf PatternLayout) {super.setLayout (maketas); } else {thrown new IllegalArgumentException ("Išdėstymas negalioja"); }}

Deja, šis požiūris taip pat turi problemų. 4 sąraše pateiktas metodas pateikia vykdymo laiko išimtį, o programos, iškvietusios šį metodą, gali būti nepasirengusios jos gaudyti. Kitaip tariant, setLayout (maketo maketas) metodas negali garantuoti, kad nebus vykdoma išimtis; todėl susilpnina garantijas (vėlesnes sąlygas), kurias suteikia metodas, kurį jis viršija. Jei pažvelgsime į tai atsižvelgdami į išankstines sąlygas, setLayout reikalauja, kad maketas būtų „PatternLayout“, todėl turi stipresnis išankstinių sąlygų, nei metodas, kurį jis viršija. Bet kokiu atveju mes pažeidėme pagrindinį objektyvaus dizaino principą, kuris yra Liskovo pakeitimo principas, naudojamas paveldėjimui apsaugoti.

Sprendimai

Tai, kad nėra lengvo sprendimo, kaip pataisyti dizainą JDBCAppender rodo, kad darbe yra gilesnė problema. Šiuo atveju pasirinktas abstrakcijos lygis kuriant pagrindinius abstrakčių tipus (visų pirma Išdėstymas) reikia tiksliai sureguliuoti. Pagrindinis metodas, apibrėžtas Išdėstymas yra formatas („LoggingEvent“ įvykis). Šis metodas pateikia eilutę. Tačiau, kai prisijungiate prie reliacinės duomenų bazės, reikia sukurti ne vieną eilutę, o vertes (eilutę).

Vienas iš galimų sprendimų būtų naudoti sudėtingesnę duomenų struktūrą kaip grąžinimo formatą tipą. Tačiau tai reikštų papildomą pridėtinę sumą tais atvejais, kai galbūt norėtumėte sukurti eilutę. Reikėtų sukurti papildomus tarpinius objektus, o vėliau surinkti šiukšles, pakenkiant medienos ruošos sistemos veikimui. Naudojant sudėtingesnį grąžinimo tipą, „Log4j“ taip pat būtų sunkiau suprasti. Paprastumas yra labai pageidaujamas dizaino tikslas.

Kitas galimas sprendimas būtų „sluoksniuotos abstrakcijos“ naudojimas naudojant du abstrakčius tipus, Pareiškėjas ir „CustomizableAppender“ kuris tęsiasi Pareiškėjas. Tik „CustomizableAppender“ tada apibrėžtų metodą setLayout (maketo maketas). JDBCAppender tik įgyvendintų Pareiškėjas, o kiti priedai, pvz., „ConsoleAppender“ įgyvendintų „CustomizableAppender“. Šio požiūrio trūkumas yra padidėjęs sudėtingumas (pvz., Kaip apdorojami „Log4j“ konfigūracijos failai) ir tai, kad kūrėjai turi priimti pagrįstą sprendimą, kurį abstrakcijos lygį naudoti anksti.

Apibendrinant

Šiame straipsnyje aš naudoju „Log4j“ kaip pavyzdį, kad pademonstruočiau tiek ortogonalumo projektavimo principą, tiek kartais kompromisą tarp dizaino principo laikymosi ir sistemos kokybės požymio, pavyzdžiui, mastelio, pasiekimo. Netgi tais atvejais, kai neįmanoma pasiekti visiško ortogonalumo, manau, kad kompromisas turėtų būti sąmoningas sprendimas ir kad jis turėtų būti tinkamai dokumentuotas (pavyzdžiui, kaip techninė skola). Žr. Skyrių Ištekliai, kad sužinotumėte daugiau apie šiame straipsnyje aptartas sąvokas ir technologijas.

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